---
title: "TypeScript"
description: "A TypeScript reference covering the type syntax engineers use most, from unions and generics to utility types, modern techniques (satisfies, const type params, template literal types, using), and the TypeScript 7 native compiler."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/typescript
---

# TypeScript

A fast reference for the TypeScript you write every day: primitives and unions, interfaces versus type aliases, generics and constraints, the built-in utility types (Partial, Pick, Omit, Record, ReturnType), type guards and narrowing, and declaration merging. It also covers modern type techniques (`satisfies`, `const` type parameters, template literal types, conditional types with `infer`, variadic tuples, `NoInfer`, and `using`) and how they behave under the TypeScript 7 native compiler.

## Basic Types

The primitive and composite types you annotate with every day.

### Primitives and unions

Annotate variables, and combine types with unions and literals.

**Keywords:** string, number, union, literal

#### Primitives and a union

```typescript
let name: string = 'Ada';
let count: number = 42;
let active: boolean = true;

// union: one of several types
let id: string | number = 'abc';
id = 123;

// literal union: one of a fixed set of values
type Status = 'idle' | 'loading' | 'done';
let s: Status = 'idle';
```

Union types allow a value to be one of several types; literal unions restrict it to specific values.

- Prefer literal unions over loose strings for state.

#### Arrays and tuples

```typescript
const nums: number[] = [1, 2, 3];
const pair: [string, number] = ['age', 30]; // tuple
```

Arrays hold many of one type; tuples fix the length and the type at each position.

### any vs unknown vs never

The three special types and when each is correct.

#### unknown forces a check

```typescript
let a: any = 5;      // opts out of checking (avoid)
let u: unknown = 5;  // must narrow before use
if (typeof u === 'number') u.toFixed(2);
```

Prefer unknown over any; it keeps type safety by forcing you to narrow before use. never represents a value that can't happen.

**Best practices:**

- Treat any as a code smell; reach for unknown and narrow.

## Interfaces and Type Aliases

The two ways to name object shapes, and when to use each.

### interface vs type

Both describe shapes; interfaces merge and extend, type aliases can express unions.

**Keywords:** interface, type, extends

#### Interface and type alias

```typescript
interface User {
  id: string;
  name: string;
  email?: string; // optional
}

type Point = {x: number; y: number};
type Id = string | number; // only a type alias can do this
```

Use interface for object shapes you may extend or implement; use type when you need unions, intersections, or mapped types.

- Interfaces with the same name merge; type aliases do not.

#### Extending and intersecting

```typescript
interface Admin extends User {
  role: 'admin';
}

type Timestamped = Point & {createdAt: number}; // intersection
```

extends adds to an interface; & intersects types into one combined shape.

### readonly and index signatures

Lock fields and model open-ended key maps.

#### readonly + index signature

```typescript
interface Config {
  readonly apiUrl: string;       // can't be reassigned
  [key: string]: string;         // any string key -> string
}
```

readonly prevents reassignment; an index signature types objects used as dictionaries.

## Generics

Reusable, type-safe building blocks parameterized by type.

### Generic functions and constraints

Write functions that work over many types while keeping the link between input and output.

**Keywords:** generic, extends, constraint

#### A generic function

```typescript
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}
const n = first([1, 2, 3]); // n: number | undefined
```

T is inferred from the argument, so the return type tracks the input type.

#### Constraining a type parameter

```typescript
function longest<T extends {length: number}>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}
```

extends constrains T to types that have a length, so the function only accepts things it can measure.

### Generic interfaces and defaults

Parameterize object shapes and provide default type arguments.

#### Generic interface with a default

```typescript
interface ApiResponse<T = unknown> {
  data: T;
  status: number;
}
const r: ApiResponse<User> = {data: user, status: 200};
```

The default type argument (= unknown) is used when the caller does not specify one.

## Built-in Utility Types

The standard-library type transformers you reach for constantly.

### Partial, Required, Readonly

Flip modifiers across every property of a type.

**Keywords:** Partial, Required, Readonly

#### Partial and Required

```typescript
type Draft = Partial<User>;    // all fields optional
type Full = Required<User>;    // all fields required
type Frozen = Readonly<User>;  // all fields readonly
```

Handy for update payloads (Partial) and for locking a value (Readonly).

### Pick, Omit, Record

Select, remove, and build key/value maps.

#### Pick, Omit, Record

```typescript
type Credentials = Pick<User, 'id' | 'email'>;   // keep some keys
type PublicUser = Omit<User, 'email'>;           // drop some keys
type UsersById = Record<string, User>;           // map of key -> User
```

Pick and Omit derive new shapes from existing ones; Record builds a dictionary type.

- Deriving types with Pick/Omit keeps them in sync with the source type.

### ReturnType, Parameters, Awaited

Extract types from functions and promises.

#### Infer from a function and a promise

```typescript
function makeUser() { return {id: '1', name: 'Ada'}; }
type NewUser = ReturnType<typeof makeUser>;
type Args = Parameters<typeof makeUser>;
type Resolved = Awaited<Promise<number>>; // number
```

These pull types out of existing values so you do not repeat yourself.

## Type Guards and Narrowing

Convince the compiler a value is a more specific type at runtime.

### typeof, in, and instanceof

The built-in ways to narrow a union.

**Keywords:** typeof, instanceof, in, narrowing

#### Narrowing a union

```typescript
function area(shape: {r: number} | {w: number; h: number}) {
  if ('r' in shape) return Math.PI * shape.r ** 2;
  return shape.w * shape.h;
}
```

The in operator narrows the union so each branch sees the right shape.

#### Custom type guard

```typescript
function isUser(x: unknown): x is User {
  return typeof x === 'object' && x !== null && 'id' in x;
}
```

A function returning `x is User` teaches the compiler to narrow after the check passes.

**Common errors:**

- **Object is possibly 'undefined'**: Narrow first (an if check or optional chaining ?.) before accessing the property.

## Modern Type Techniques

Newer type-level tools that make TypeScript expressive, all current under the TypeScript 7 compiler.

### The satisfies operator

Check a value against a type without widening it, so you keep the precise inferred type.

**Keywords:** satisfies, inference

#### satisfies keeps the narrow type

```typescript
type Route = Record<string, {method: 'GET' | 'POST'}>;

// annotation widens: method is 'GET' | 'POST'
const a: Route = {home: {method: 'GET'}};

// satisfies checks AND keeps the literal type
const routes = {home: {method: 'GET'}} satisfies Route;
routes.home.method; // narrowed to 'GET'
```

satisfies validates the value against the type but leaves the inferred type intact, so you get checking without losing literal precision.

### const type parameters

Infer the narrowest literal, readonly type for a generic argument.

#### A const generic

```typescript
function tuple<const T extends readonly unknown[]>(...args: T): T {
  return args;
}
const t = tuple('a', 1); // readonly ['a', 1], not (string | number)[]
```

The const modifier tells TypeScript to infer literal, readonly types instead of widening them.

### Template literal types

Build string types by interpolating other types.

**Keywords:** template literal, string type

#### Typed event handler names

```typescript
type Event = 'click' | 'focus';
type Handler = `on${Capitalize<Event>}`; // 'onClick' | 'onFocus'
```

Template literal types plus the intrinsic helpers (Uppercase, Lowercase, Capitalize) generate precise string unions.

### Mapped types with key remapping

Transform both the keys and values of a type with the as clause.

#### Generate getters from a type

```typescript
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type G = Getters<{name: string}>; // {getName: () => string}
```

The as clause in a mapped type renames keys; mapping a key to never filters it out entirely.

### Conditional types and infer

Pick a type based on a condition and extract pieces with infer.

**Keywords:** conditional, infer, extends

#### Unwrap an array element type

```typescript
type ElementOf<T> = T extends (infer U)[] ? U : never;
type N = ElementOf<number[]>; // number
```

A conditional type (T extends X ? A : B) combined with infer pulls a type out of another type.

### Variadic tuple types

Spread and compose tuple types, handy for typing concat, curry, and the like.

#### Prepend an element to a tuple

```typescript
type Prepend<T, Arr extends readonly unknown[]> = [T, ...Arr];
type R = Prepend<0, [1, 2]>; // [0, 1, 2]
```

The ...rest spread inside tuple types composes fixed and variable-length tuples.

### NoInfer

Stop a type parameter from being inferred from one position.

#### Force inference from the first argument

```typescript
function paint<C extends string>(colors: C[], fallback: NoInfer<C>): void {}
paint(['red', 'blue'], 'red'); // fallback constrained to 'red' | 'blue'
```

NoInfer<T> (built in since TypeScript 5.4) prevents the compiler from widening C based on the fallback argument.

### Explicit resource management (using)

Deterministically dispose resources with using and await using.

**Keywords:** using, Disposable, Symbol.dispose

#### using a disposable

```typescript
function openFile(path: string): Disposable & {read(): string} {
  return {read: () => '...', [Symbol.dispose]() {/* close */}};
}

function run() {
  using file = openFile('a.txt');
  file.read();
} // Symbol.dispose() runs automatically at scope exit
```

using (and await using for Symbol.asyncDispose) calls the disposer when the scope exits, like RAII, so you never forget to clean up.

## The TypeScript 7 Native Compiler

What the native (Go) compiler changes, and what it deliberately does not.

### tsc on the native compiler

TypeScript 7 is a native port of the compiler focused on speed, with the same type-checking semantics as 6.x.

**Keywords:** typescript 7, native, performance

#### Same commands, much faster

```bash
# Type-check only, no emit
tsc --noEmit

# Fast feedback in watch mode
tsc --watch
```

TypeScript 7 (GA July 2026) is a native Go port that type-checks roughly 8-12x faster than 6.x on large projects. It is a port, not a rewrite, so your types and tsconfig keep working unchanged.

- Because 7.0 preserves semantics, everything else in this cheatsheet is still valid.
- TypeScript 6.0 was the transition release that turned earlier deprecations and stricter defaults on; clear those warnings before moving to 7.

**Best practices:**

- Set "strict": true in tsconfig; TypeScript 7 leans on the stricter defaults.

**Advanced notes:**

- **One tooling caveat:** The 7.0 native compiler shipped without a stable programmatic API, so some ecosystem tools that call the compiler API directly may need to wait for a later 7.x release. Editor type-checking and tsc itself work today.

## Modules and Declaration Merging

Organize types across files and extend existing ones.

### Type-only imports

Import and export types without pulling in runtime code.

#### import type

```typescript
import type {User} from './models';
export type {User};
```

import type is erased at compile time, so it never affects the JS bundle.

### Declaration merging

Add to an existing interface, including third-party ones.

#### Augment an interface

```typescript
interface Window {
  analytics?: {track(event: string): void};
}
```

Re-declaring an interface merges the members, which is how you augment globals or library types.

**Advanced notes:**

- **Why merging is interface-only:** Interfaces are open and merge by name; type aliases are closed and would error on redeclaration. That is the main reason to prefer interface for extensible, shared shapes.
