TypeScript Cheatsheet

Types

Use this TypeScript reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Primitive and Special Types

If you come from Java, C++, or another typed programming language course, map those instincts carefully: TypeScript checks types at compile time but still runs as JavaScript. Prefer precise types over any, then narrow unknown data before using it.

// Primitives
let s: string = "hello";
let n: number = 42;
let b: boolean = true;
let big: bigint = 100n;
let sym: symbol = Symbol("key");
let u: undefined = undefined;
let nil: null = null;

// Special
let a: any = "anything";        // disables type checking
let uk: unknown = "safe top type"; // must narrow before use
let v: void = undefined;        // function return type
function boom(): never { throw new Error(); }  // never returns

Array Types

// Two equivalent syntaxes
let a: number[] = [1, 2, 3];
let b: Array<number> = [1, 2, 3];

// Readonly arrays (immutable at type level)
let r: readonly number[] = [1, 2, 3];
let r2: ReadonlyArray<number> = [1, 2, 3];

// Array of unions
let mixed: (string | number)[] = [1, "two", 3];

// Multidimensional
let matrix: number[][] = [[1, 2], [3, 4]];

// Inferred
let inferred = [1, 2, 3]; // number[]

Tuple Types

// Basic tuple — fixed-length, ordered types
let point: [number, number] = [0, 0];

// Labeled tuple (TS 4.0+) — labels are documentation only
let range: [start: number, end: number] = [0, 100];

// Optional element (must be last)
let optional: [string, number?] = ["hello"];

// Rest element
let atLeastOne: [number, ...number[]] = [1, 2, 3];
let stringThenNums: [string, ...number[]] = ["label", 1, 2, 3];

// Readonly tuple
let fixed: readonly [number, string] = [1, "a"];

// Destructuring tuples
const [x, y] = point;
const [head, ...tail]: [string, ...number[]] = ["a", 1, 2];

Object Types

// Inline object type
let user: { name: string; age: number } = { name: "Alice", age: 30 };

// Optional property
let config: { host: string; port?: number } = { host: "localhost" };

// Readonly property
let point: { readonly x: number; readonly y: number } = { x: 0, y: 0 };
// point.x = 1; // Error

// Index signature — dynamic keys of a known type
let record: { [key: string]: number } = {};
record["foo"] = 1;

// Index signature with known properties
let mixed: { length: number; [key: string]: number } = { length: 0 };

// Object with method
let obj: { greet(name: string): void } = {
  greet(name) { console.log(`Hi ${name}`); }
};

Union Types

// Value can be one of several types
let id: string | number;
id = "abc";
id = 123;

// Discriminated union (tagged union)
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect"; width: number; height: number };

function area(s: Shape): number {
  if (s.kind === "circle") return Math.PI * s.radius ** 2;
  return s.width * s.height;
}

// Union with null (common pattern)
type MaybeString = string | null;
type Optional<T> = T | undefined;

Intersection Types

// Combines all properties of multiple types
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // { name: string; age: number }

const p: Person = { name: "Alice", age: 30 };

// Merging with additional props
type Admin = Person & { role: "admin" };

// Intersecting function types (overload-like)
type Stringify = ((n: number) => string) & ((b: boolean) => string);

Literal Types

// String literal
type Direction = "up" | "down" | "left" | "right";
let dir: Direction = "up";

// Number literal
type DiceValue = 1 | 2 | 3 | 4 | 5 | 6;

// Boolean literal
type AlwaysTrue = true;

// Template literal type
type CSSUnit = `${number}px` | `${number}em` | `${number}rem`;
type EventHandler = `on${Capitalize<string>}`;

// Widening vs narrowing
const a = "hello";  // type: "hello" (literal, const)
let b = "hello";    // type: string (widened, let)
let c = "hello" as const; // type: "hello"

as const and Const Assertions

// Primitives: infer literal type
const x = 42 as const; // 42

// Objects: all properties become readonly literals
const config = { host: "localhost", port: 3000 } as const;
// { readonly host: "localhost"; readonly port: 3000 }

// Arrays: become readonly tuples
const dirs = ["up", "down"] as const;
// readonly ["up", "down"]

// Use to derive union from an object
const STATUS = { ACTIVE: 1, INACTIVE: 2 } as const;
type Status = typeof STATUS[keyof typeof STATUS]; // 1 | 2

// Use to derive union from array
const ROLES = ["admin", "user", "guest"] as const;
type Role = typeof ROLES[number]; // "admin" | "user" | "guest"

Type Aliases with type

type UserId = string;
type Point = { x: number; y: number };
type Nullable<T> = T | null;
type Callback = (err: Error | null, data: string) => void;

// Recursive type alias
type Json =
  | string
  | number
  | boolean
  | null
  | Json[]
  | { [key: string]: Json };

// Conditional type alias
type IsString<T> = T extends string ? true : false;

Conditional Types

// Basic conditional
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<number[]>; // true
type B = IsArray<string>;   // false

// infer keyword — extract a type from a condition
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type C = UnpackArray<number[]>; // number
type D = UnpackArray<string>;   // string

// Distributive conditional types — distributes over unions
type Flatten<T> = T extends any[] ? T[number] : T;
type E = Flatten<string[] | number>; // string | number

// NonNullable implementation
type MyNonNullable<T> = T extends null | undefined ? never : T;

// ReturnType implementation
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Parameters implementation
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;

// Exclude null from union
type WithoutNull = string | null | undefined extends infer T
  ? T extends null | undefined ? never : T
  : never;

Mapped Types

// Make all properties optional
type Partial<T> = { [K in keyof T]?: T[K] };

// Make all properties required
type Required<T> = { [K in keyof T]-?: T[K] };

// Make all properties readonly
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// Map to a different value type
type Stringify<T> = { [K in keyof T]: string };

// Filter keys (conditional mapped type)
type OnlyStrings<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K]
};

// Remap keys with as
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

// Add/remove modifiers
type Mutable<T> = { -readonly [K in keyof T]: T[K] };  // remove readonly
type AllRequired<T> = { [K in keyof T]-?: T[K] };       // remove optional

Indexed Access Types

type Person = { name: string; age: number; address: { city: string } };

type NameType = Person["name"];    // string
type AgeType = Person["age"];      // number
type CityType = Person["address"]["city"]; // string

// Union of property types
type PersonValues = Person[keyof Person]; // string | number | { city: string }

// Array element type
type Arr = string[];
type Elem = Arr[number]; // string

// Tuple element types
type Tuple = [string, number, boolean];
type First = Tuple[0]; // string
type TupleUnion = Tuple[number]; // string | number | boolean

Recursive and Self-Referential Types

// Recursive type
type NestedArray<T> = T | NestedArray<T>[];

// Linked list
type ListNode<T> = { value: T; next: ListNode<T> | null };

// Tree
type Tree<T> = { value: T; children: Tree<T>[] };

// Deep partial
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

// JSON type
type Json =
  | string | number | boolean | null
  | Json[]
  | { [key: string]: Json };

Infer Keyword

// Extract return type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type R = ReturnType<() => string>; // string

// Extract first argument
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;
type F = FirstArg<(a: number, b: string) => void>; // number

// Extract promise value
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;
type A = Awaited<Promise<Promise<string>>>; // string

// Extract array element
type ElementType<T> = T extends (infer E)[] ? E : never;
type E = ElementType<number[]>; // number

// Infer in multiple positions
type Swap<T> = T extends [infer A, infer B] ? [B, A] : T;
type S = Swap<[string, number]>; // [number, string]

Type Guards and Narrowing Types

// typeof narrowing
function process(val: string | number) {
  if (typeof val === "string") val.toUpperCase(); // string
  else val.toFixed();                             // number
}

// instanceof narrowing
function handle(err: Error | TypeError) {
  if (err instanceof TypeError) err.message; // TypeError
}

// User-defined type predicate
function isString(val: unknown): val is string {
  return typeof val === "string";
}

// Assertion function
function assertDefined<T>(val: T | undefined): asserts val is T {
  if (val === undefined) throw new Error("Expected defined");
}

// in narrowing
function printId(id: { id: number } | { uid: string }) {
  if ("id" in id) console.log(id.id);
  else console.log(id.uid);
}