TypeScript Cheatsheet

TypeScript Cheatsheet

Complete reference — types, generics, utility types, patterns, async, decorators, and interview-ready snippets.

Primitive & Built-in Types

All Primitive Types + Type Assertions + Literals

Basics
// ─── Primitive Types ─────────────────────────────────────── let name: string = "Alice"; let age: number = 30; // integer AND float — no separate int type let active: boolean = true; let id: bigint = 9007199254740991n; let sym: symbol = Symbol("key"); let nothing: null = null; let undef: undefined = undefined; // ─── Special Types ───────────────────────────────────────── let anything: any; // ⚠ disables type checking — avoid let safe: unknown; // must narrow before use — prefer over any function fail(): never { // function that never returns throw new Error("boom"); } function log(): void {} // returns nothing // ─── Literal Types ───────────────────────────────────────── type Direction = "north" | "south" | "east" | "west"; type Status = 200 | 400 | 404 | 500; type Enabled = true; // literal boolean // ─── Type Assertions ─────────────────────────────────────── const el = document.getElementById("btn") as HTMLButtonElement; const len = (anything as string).length; const num = <number>anything; // JSX-incompatible syntax, prefer `as` const sure = anything!; // non-null assertion — asserts not null/undefined // ─── const assertion ─────────────────────────────────────── const config = { host: "localhost", port: 3000 } as const; // config.host is "localhost" (literal), not string — fully readonly // ─── Arrays & Tuples ─────────────────────────────────────── const nums: number[] = [1, 2, 3]; const strs: Array<string> = ["a", "b"]; const pair: [string, number] = ["age", 30]; // tuple — exact length+types const triple: [string, number, boolean?] = ["x", 1]; // optional 3rd
Objects, Interfaces & Type Aliases

interface vs type — Differences + Extending + Index Signatures

Core
// ─── interface (preferred for object shapes & public API) ── interface User { readonly id: number; // readonly — cannot reassign after init name: string; email?: string; // optional property greet(): string; // method signature } // Extending interfaces (open for extension — key difference from type) interface Admin extends User { role: "super" | "moderator"; } // Interface merging (declaration merging — unique to interface) interface Window { myLib: string } // extends existing Window global // ─── type alias (preferred for unions, primitives, mapped types) ── type ID = string | number; type Point = { x: number; y: number }; type AdminUser = User & { role: string }; // intersection // ─── Index Signatures ────────────────────────────────────── interface StringMap { [key: string]: string; // any string key → string value } type Record2<K extends string, V> = { [P in K]: V }; // ─── Nested + Optional chaining ─────────────────────────── interface Config { db?: { host: string; port?: number }; } const port = config.db?.port ?? 5432; // optional chain + nullish coalescing // ─── Satisfies operator (TS 4.9+) ───────────────────────── const palette = { red: [255, 0, 0], green: "#00ff00", } satisfies Record<string, string | number[]>; // palette.red is still number[] (not string | number[]) — narrowed!
Unions, Intersections & Type Narrowing

Discriminated Unions + All Narrowing Techniques

Critical
// ─── typeof narrowing ────────────────────────────────────── function format(val: string | number) { if (typeof val === "string") return val.toUpperCase(); // val: string here return val.toFixed(2); // val: number here } // ─── instanceof narrowing ────────────────────────────────── function handleError(e: unknown) { if (e instanceof Error) return e.message; // e: Error return String(e); } // ─── in narrowing ────────────────────────────────────────── type Fish = { swim(): void }; type Bird = { fly(): void }; function move(animal: Fish | Bird) { if ("swim" in animal) animal.swim(); // animal: Fish else animal.fly(); // animal: Bird } // ─── Discriminated Union (most powerful pattern) ─────────── type Shape = | { kind: "circle"; radius: number } | { kind: "rectangle"; width: number; height: number } | { kind: "triangle"; base: number; height: number }; function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "rectangle": return s.width * s.height; case "triangle": return 0.5 * s.base * s.height; default: const _exhaustive: never = s; // compile error if case missing! return _exhaustive; } } // ─── Type Guard (user-defined) ───────────────────────────── function isFish(a: Fish | Bird): a is Fish { return (a as Fish).swim !== undefined; } // ─── Assertion Functions (TS 3.7+) ───────────────────────── function assert(cond: unknown, msg: string): asserts cond { if (!cond) throw new Error(msg); } function assertString(v: unknown): asserts v is string { if (typeof v !== "string") throw new TypeError(); }
Generics

Generics — Functions, Interfaces, Constraints, Defaults, Conditional

Critical
// ─── Generic function ────────────────────────────────────── function identity<T>(arg: T): T { return arg; } identity<string>("hello"); // explicit identity(42); // inferred T=number // ─── Generic interface ───────────────────────────────────── interface ApiResponse<T> { data: T; status: number; error?: string; } type UserResponse = ApiResponse<User>; // ─── Constraints (extends) ───────────────────────────────── function getLength<T extends { length: number }>(arg: T): number { return arg.length; } getLength("hello"); // ✓ string has length getLength([1, 2, 3]); // ✓ array has length // ─── keyof constraint ────────────────────────────────────── function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } getProperty({ a: 1, b: "two" }, "a"); // returns number // ─── Default type params (TS 2.3+) ───────────────────────── interface Container<T = string> { value: T } const c: Container = { value: "hello" }; // T defaults to string // ─── Conditional Types ───────────────────────────────────── type IsArray<T> = T extends any[] ? "yes" : "no"; type A = IsArray<number[]>; // "yes" type B = IsArray<string>; // "no" // Infer within conditional type UnwrapArray<T> = T extends (infer U)[] ? U : T; type Num = UnwrapArray<number[]>; // number type Str = UnwrapArray<string>; // string (not array, so T returned) // ReturnType built with infer type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never;
Utility Types

All Built-in Utility Types with Examples

Must Know
interface User { id: number; name: string; email: string; age?: number } // ─── Object Transformation ──────────────────────────────── type PartialUser = Partial<User>; // all props optional type RequiredUser = Required<User>; // all props required (removes ?) type ReadonlyUser = Readonly<User>; // all props readonly type UserRecord = Record<string, User>; // dictionary // Pick & Omit — most used in practice type UserPreview = Pick<User, "id" | "name">; // only id & name type CreateUser = Omit<User, "id">; // everything except id type UpdateUser = Partial<Omit<User, "id">>; // common patch body // ─── Set operations on Union types ──────────────────────── type AB = "a" | "b" | "c"; type Exl = Exclude<AB, "a">; // "b" | "c" — removes from union type Ext = Extract<AB, "a" | "d">; // "a" — keeps intersection type NN = NonNullable<string | null | undefined>; // string // ─── Function types ─────────────────────────────────────── type Fn = (x: number) => string; type RT = ReturnType<Fn>; // string type PT = Parameters<Fn>; // [x: number] type CT = ConstructorParameters<typeof Date>; // Date constructor args type IT = InstanceType<typeof Date>; // Date instance type // ─── Awaited (TS 4.5+) ──────────────────────────────────── type Resolved = Awaited<Promise<Promise<string>>>; // string (deeply unwraps) // ─── Template Literal Types (TS 4.1+) ──────────────────── type EventName<T extends string> = `on${Capitalize<T>}`; type ClickEvent = EventName<"click">; // "onClick" type CRUD = "create" | "read" | "update" | "delete"; type APIRoute = `/api/${CRUD}`; // "/api/create" | "/api/read" | ...
Functions

Function Overloads, Rest, Callbacks, Higher-Order, this parameter

Functions
// ─── Basic signatures ────────────────────────────────────── function add(a: number, b: number): number { return a + b; } const mul = (a: number, b: number): number => a * b; // Optional + default + rest function greet(name: string, greeting = "Hello", ...titles: string[]): string { return `${greeting} ${titles.join(" ")} ${name}`; } // ─── Function Overloads ──────────────────────────────────── function parse(input: string): number; function parse(input: number): string; function parse(input: string | number): string | number { // implementation return typeof input === "string" ? Number(input) : String(input); } // ─── Callback types ─────────────────────────────────────── type Predicate<T> = (item: T) => boolean; type Mapper<T, U> = (item: T, index: number) => U; function filter<T>(arr: T[], pred: Predicate<T>): T[] { return arr.filter(pred); } // ─── this parameter (fake param for type of `this`) ──────── interface Counter { count: number; increment(this: Counter): void } const counter: Counter = { count: 0, increment() { this.count++; } // `this` is typed as Counter }; // ─── Higher-order / Compose ──────────────────────────────── const compose = <A, B, C>(f: (b: B) => C, g: (a: A) => B) => (a: A) => f(g(a)); const doubleStr = compose((n: number) => n.toString(), (n: number) => n * 2);
Classes

Classes — Access Modifiers, Abstract, Mixins, Decorators

OOP
abstract class Animal { // Access modifiers: public (default) | private | protected | readonly constructor( public readonly name: string, // shorthand property declaration protected age: number, private _secret: string = "hidden" ) {} abstract speak(): string; // must implement in subclass describe(): string { return `${this.name} is ${this.age}`; } static create(name: string): this { // polymorphic this in factory return new (this as any)(name, 0); } } class Dog extends Animal { speak(): string { return `${this.name} says Woof`; } // Override: can access this.age (protected) but not this._secret (private) } // ─── Implements interface ────────────────────────────────── interface Serializable { serialize(): string } class Cat extends Animal implements Serializable { speak() { return "Meow"; } serialize() { return JSON.stringify({ name: this.name }); } } // ─── Private fields (runtime private — ES2022) ───────────── class BankAccount { #balance = 0; // truly private — not on prototype deposit(n: number) { this.#balance += n; } get balance() { return this.#balance; } } // ─── Mixin pattern ───────────────────────────────────────── type Constructor<T = {}> = new (...args: any[]) => T; function Timestamped<TBase extends Constructor>(Base: TBase) { return class extends Base { createdAt = new Date(); }; } class TimestampedUser extends Timestamped(Animal) { speak() { return "..."; } }
Async / Promises / Error Handling

Async/Await, Promise combinators, Error handling patterns

Critical
// ─── Basic async/await ───────────────────────────────────── async function fetchUser(id: number): Promise<User> { const res = await fetch(`/api/users/${id}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json() as Promise<User>; } // ─── Promise combinators ─────────────────────────────────── // all — all succeed or reject fast on first failure const [u1, u2] = await Promise.all([fetchUser(1), fetchUser(2)]); // allSettled — wait for all, get status for each (never rejects) const results = await Promise.allSettled([fetchUser(1), fetchUser(99)]); results.forEach(r => { if (r.status === "fulfilled") console.log(r.value); else console.error(r.reason); }); // race — first settled wins const fastest = await Promise.race([fetchUser(1), fetchUser(2)]); // any — first FULFILLED wins (ignores rejections; rejects only if ALL fail) const first = await Promise.any([fetchUser(1), fetchUser(2)]); // ─── Result type pattern (safer than try/catch everywhere) ─ type Result<T, E = Error> = | { ok: true; value: T } | { ok: false; error: E }; async function safeGet<T>(fn: () => Promise<T>): Promise<Result<T>> { try { return { ok: true, value: await fn() }; } catch (e) { return { ok: false, error: e instanceof Error ? e : new Error(String(e)) }; } } // ─── Async iterator / generator ──────────────────────────── async function* paginate(url: string) { let page = 1; while (true) { const data = await fetch(`${url}?page=${page++}`).then(r => r.json()); if (!data.length) break; yield data; } } for await (const page of paginate("/api/items")) { console.log(page); }
Advanced Types

Mapped Types, Template Literals, Decorators, Module Augmentation

Advanced
// ─── Mapped Types ────────────────────────────────────────── type Nullable<T> = { [P in keyof T]: T[P] | null }; type Getters<T> = { [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P] }; // Getters<{name:string}> → { getName: () => string } // ─── Recursive types ─────────────────────────────────────── type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]; }; type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; }; // ─── Decorators (TS + experimentalDecorators) ────────────── function log(target: any, key: string, desc: PropertyDescriptor) { const original = desc.value; desc.value = function(...args: any[]) { console.log(`Calling ${key} with`, args); return original.apply(this, args); }; } class Service { @log fetchData(id: number) { /* ... */ } } // ─── Branded / Opaque types ──────────────────────────────── type UserId = string & { readonly _brand: "UserId" }; type OrderId = string & { readonly _brand: "OrderId" }; const makeUserId = (id: string): UserId => id as UserId; // Prevents accidentally passing UserId where OrderId expected

Enums — const enum, union alternative, reverse mapping

Enums
// Numeric enum (default — auto-increments) enum Direction { Up, Down, Left, Right } // 0,1,2,3 Direction.Up // 0 Direction[0] // "Up" — reverse mapping (numeric only!) // String enum (most common — no reverse mapping, clear values) enum Status { Active = "ACTIVE", Inactive = "INACTIVE" } // const enum — inlined at compile time (zero runtime overhead) const enum Color { Red, Green, Blue } let c = Color.Red; // compiled to: let c = 0; // ⚠ Modern preference: union of string literals (simpler, no runtime object) type StatusLiteral = "ACTIVE" | "INACTIVE"; // Avoids enum pitfalls (numeric assignability, bloated JS output)
Common TypeScript Patterns

Builder, Repository, Event Emitter, Dependency Injection in TypeScript

Patterns
// ─── Builder Pattern ─────────────────────────────────────── class QueryBuilder { private parts: string[] = []; select(...cols: string[]): this { this.parts.push(`SELECT ${cols.join(", ")}`); return this; } from(table: string): this { this.parts.push(`FROM ${table}`); return this; } where(cond: string): this { this.parts.push(`WHERE ${cond}`); return this; } build(): string { return this.parts.join(" "); } } const q = new QueryBuilder().select("id","name").from("users").where("active=1").build(); // ─── Generic Repository ──────────────────────────────────── interface Repository<T, ID> { findById(id: ID): Promise<T | null>; findAll(): Promise<T[]>; save(entity: T): Promise<T>; delete(id: ID): Promise<void>; } class UserRepository implements Repository<User, number> { /* ... */ } // ─── Typed Event Emitter ─────────────────────────────────── type Events = { login: [userId: string]; logout: [userId: string, reason: string]; error: [err: Error]; }; class TypedEmitter<E extends Record<string, any[]>> { private listeners = new Map<string, Function[]>(); on<K extends keyof E>(event: K, cb: (...args: E[K]) => void) { (this.listeners.get(event as string) ?? this.listeners.set(event as string, []).get(event as string)!).push(cb); } emit<K extends keyof E>(event: K, ...args: E[K]) { this.listeners.get(event as string)?.forEach(cb => cb(...args)); } }
Common Gotchas & Interview Tips

Top TypeScript Gotchas Every Developer Must Know

Interview

Structural vs Nominal Typing

interface Cat { name: string } interface Dog { name: string } const dog: Dog = { name: "Rex" }; const cat: Cat = dog; // ✓ structural — shapes match

Excess Property Checks

const u: User = { id:1, name:"A", extra:"x" }; // ✗ Error const obj = { id:1, name:"A", extra:"x" }; const u2: User = obj; // ✓ via variable (no excess check)

Type vs typeof

const str = "hello"; type T1 = typeof str; // "hello" (literal) type T2 = ReturnType<typeof fetch>; // runtime value → type

Widening / Narrowing

let x = "hello"; // widened to string const y = "hello"; // narrowed to "hello" literal let z = null; // widened to null (not never)
Top Interview Points
  • interface vs type: Use interface for object shapes (extensible via declaration merging). Use type for unions, intersections, primitives, mapped types.
  • any vs unknown: unknown requires narrowing before use — much safer. Never use any in production code.
  • Discriminated union + exhaustive check with never is the best pattern for state machines.
  • const enum inlines values (no runtime overhead) but breaks with isolatedModules — avoid in libraries.
  • Type assertions (as) bypass type checking — use sparingly. Prefer type guards or generic constraints.
  • strictNullChecks: Always on in production — forces explicit handling of null/undefined.
  • Branded types prevent semantic errors (passing userId where productId expected).