TypeScript Cheatsheet

Types

Use this TypeScript reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

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);
}