TypeScript Cheatsheet

Complete development reference

Development
Contents
๐Ÿ“ฆ

Basic Types

// Primitives
let isDone: boolean = false;
let age: number = 25;
let name: string = "hello";
let big: bigint = 100n;
let sym: symbol = Symbol("id");

// Arrays
let nums: number[] = [1, 2, 3];
let names: Array<string> = ["a", "b"];

// Tuple
let pair: [string, number] = ["age", 25];
let named: [name: string, age: number] = ["Bob", 25];

// Special Types
let anything: any = 42;            // Avoid โ€” disables type checking
let notSure: unknown = 42;        // Safe any โ€” must narrow before use
function fail(): never { throw new Error(); }
function log(): void { console.log("hi"); }

// Null & Undefined
let u: undefined = undefined;
let n: null = null;

// Object & Record
let obj: object = {};
let rec: Record<string, number> = { a: 1 };

// Literal Types
let dir: "north" | "south" | "east" | "west";
let status: 200 | 404 | 500;
๐Ÿ”

Type Assertions & Narrowing

// Type Assertions
const input = document.getElementById("input") as HTMLInputElement;

// Non-null Assertion
const el = document.getElementById("app")!;

// Custom Type Guard
function isString(val: unknown): val is string {
  return typeof val === "string";
}

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

// instanceof narrowing
if (err instanceof Error) console.log(err.message);

// in narrowing
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function move(a: Fish | Bird) {
  if ("swim" in a) a.swim();
  else a.fly();
}

// Discriminated Unions
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

function area(s: Shape): number {
  switch (s.kind) {
    case "circle": return Math.PI * s.radius ** 2;
    case "square": return s.side ** 2;
  }
}

// satisfies (TS 5+)
const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
} satisfies Record<string, string | number[]>;
๐Ÿ“

Interfaces & Type Aliases

// Interface
interface User {
  readonly id: number;
  name: string;
  email?: string;                      // optional
  greet(): string;
  [key: string]: unknown;              // index signature
}

// Extending
interface Admin extends User {
  role: "admin" | "superadmin";
}

// Type Alias
type Point = { x: number; y: number };
type ID = string | number;
type Callback = (data: string) => void;

// Intersection
type Employee = User & { department: string };

// Declaration Merging (interface only)
interface Config { debug: boolean; }
interface Config { verbose: boolean; }
// Config now has both debug and verbose
๐Ÿ’ก Interface vs Type Use interface for extendable object shapes (supports declaration merging). Use type for unions, intersections, mapped types, and everything else.
๐Ÿงฌ

Generics

// Generic Function
function identity<T>(arg: T): T { return arg; }
const r = identity<string>("hello");
const inferred = identity(42);  // T inferred as number

// Constraints
function getLen<T extends { length: number }>(arg: T): number {
  return arg.length;
}

// Generic Interface
interface Repo<T> {
  find(id: string): Promise<T>;
  save(entity: T): Promise<void>;
  delete(id: string): Promise<boolean>;
}

// Generic Class
class Stack<T> {
  private items: T[] = [];
  push(item: T): void { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}

// Multiple Type Parameters
function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b } as T & U;
}

// Default Type Parameters
interface ApiRes<T = any, E = Error> {
  data?: T;
  error?: E;
  status: number;
}

// keyof constraint
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
๐Ÿ› ๏ธ

Utility Types

TypeDescriptionExample
Partial<T>All properties optionalPartial<User>
Required<T>All properties requiredRequired<User>
Readonly<T>All properties readonlyReadonly<User>
Pick<T, K>Select subset of keysPick<User, "id" | "name">
Omit<T, K>Exclude subset of keysOmit<User, "email">
Record<K, V>Key-value map typeRecord<string, boolean>
Exclude<U, E>Remove from unionExclude<"a"|"b", "a"> โ†’ "b"
Extract<U, E>Keep from unionExtract<"a"|"b", "a"> โ†’ "a"
NonNullable<T>Remove null/undefinedNonNullable<string | null>
ReturnType<F>Function return typeReturnType<typeof fn>
Parameters<F>Function param typesParameters<typeof fn>
Awaited<T>Unwrap Promise typeAwaited<Promise<string>>
InstanceType<C>Class instance typeInstanceType<typeof Error>
๐Ÿท๏ธ

Enums

// String Enum (preferred)
enum Status {
  Active = "ACTIVE",
  Inactive = "INACTIVE",
  Pending = "PENDING",
}

// Const Enum (inlined at compile time)
const enum Color {
  Red = "RED",
  Green = "GREEN",
}

// โœ… Modern: use 'as const' objects instead of enums
const DIRECTIONS = {
  Up: "UP",
  Down: "DOWN",
  Left: "LEFT",
  Right: "RIGHT",
} as const;

type Direction = typeof DIRECTIONS[keyof typeof DIRECTIONS];
// "UP" | "DOWN" | "LEFT" | "RIGHT"
โš™๏ธ

Functions

// Function Types
type MathOp = (a: number, b: number) => number;
const add: MathOp = (a, b) => a + b;

// Optional & Default Parameters
function greet(name: string, greeting = "Hello"): string {
  return `${greeting}, ${name}`;
}

// Rest Parameters
function sum(...nums: number[]): number {
  return nums.reduce((a, b) => a + b, 0);
}

// Overloads
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string {
  return typeof value === "string" ? value.trim() : value.toFixed(2);
}

// Generic Arrow (TSX-compatible)
const id = <T,>(arg: T): T => arg;
๐Ÿ—๏ธ

Classes

class Animal {
  public name: string;
  protected species: string;
  private _age: number;
  readonly id: string;
  static count = 0;

  constructor(name: string, species: string, age: number) {
    this.name = name;
    this.species = species;
    this._age = age;
    this.id = crypto.randomUUID();
    Animal.count++;
  }

  // Getter / Setter
  get age(): number { return this._age; }
  set age(v: number) { if (v < 0) throw new Error(); this._age = v; }

  speak(): string { return `${this.name} makes a sound`; }
}

// Inheritance
class Dog extends Animal {
  constructor(name: string, age: number, public breed: string) {
    super(name, "Canine", age);
  }
  override speak() { return `${this.name} barks`; }
}

// Abstract Class
abstract class Shape {
  abstract area(): number;
  describe() { return `Area: ${this.area()}`; }
}

// Implements Interface
interface Serializable { serialize(): string; }
class Config implements Serializable {
  constructor(private data: Record<string, unknown>) {}
  serialize() { return JSON.stringify(this.data); }
}
๐Ÿ“

Modules

// Named Exports
export const PI = 3.14159;
export function add(a: number, b: number) { return a + b; }
export interface Config { debug: boolean; }

// Default Export
export default class Logger { /* ... */ }

// Re-exports
export { default as Logger } from "./logger";
export * from "./utils";
export type { Config } from "./config";  // type-only

// Imports
import Logger from "./logger";
import { add, type Config } from "./utils";
import * as Utils from "./utils";

// Dynamic Import
const mod = await import("./heavy-module");

// Ambient / Global Declarations
declare module "untyped-lib" {
  export function doSomething(): void;
}
declare global {
  interface Window { myApp: { version: string }; }
}
๐ŸŽ€

Decorators

// Class Decorator
function sealed(ctor: Function) {
  Object.seal(ctor);
  Object.seal(ctor.prototype);
}

@sealed
class Greeter { greeting = "hello"; }

// Method Decorator
function log(target: any, key: string, desc: PropertyDescriptor) {
  const orig = desc.value;
  desc.value = function(...args: any[]) {
    console.log(`Call ${key}`, args);
    return orig.apply(this, args);
  };
}

class Calc {
  @log
  add(a: number, b: number) { return a + b; }
}
๐Ÿง 

Advanced Types

// Mapped Types
type Optional<T> = { [K in keyof T]?: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };

// Template Literal Types
type EventName = `on${Capitalize<"click" | "focus" | "blur">}`;
// "onClick" | "onFocus" | "onBlur"

// Conditional Types
type Flatten<T> = T extends Array<infer U> ? U : T;

// Distributive Conditional
type ToArray<T> = T extends any ? T[] : never;
// ToArray<string | number> โ†’ string[] | number[]

// infer keyword
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

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

// Branded / Nominal Types
type USD = number & { __brand: "USD" };
type EUR = number & { __brand: "EUR" };
function usd(n: number): USD { return n as USD; }

// const type parameters (TS 5.0+)
function asConst<const T>(value: T): T { return value; }
โณ

Async / Promises

// Basic async/await
async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

// Promise.all โ€” parallel execution
const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()]);

// Promise.allSettled โ€” no short-circuit
const results = await Promise.allSettled([a(), b()]);
results.forEach(r => {
  if (r.status === "fulfilled") console.log(r.value);
  else console.error(r.reason);
});

// Promise.race โ€” first to settle
const fastest = await Promise.race([a(), b()]);

// Async Generator
async function* paginate<T>(url: string): AsyncGenerator<T[]> {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page++}`);
    const data: T[] = await res.json();
    if (!data.length) break;
    yield data;
  }
}

for await (const batch of paginate<User>("/api/users")) {
  console.log(batch);
}
๐Ÿšจ

Error Handling

// Custom Error
class AppError extends Error {
  constructor(
    message: string,
    public code: string,
    public statusCode = 500
  ) {
    super(message);
    this.name = "AppError";
  }
}

// Result Type Pattern (no exceptions)
type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return { ok: false, error: "Division by zero" };
  return { ok: true, value: a / b };
}

// Type-safe catch
try {
  riskyOp();
} catch (err) {
  if (err instanceof AppError) console.error(err.code);
  else if (err instanceof Error) console.error(err.message);
  else console.error("Unknown", err);
}
โš™๏ธ

tsconfig.json

{
  "compilerOptions": {
    // Target & Module
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],

    // Strict (always enable)
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,

    // Output
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true,

    // Interop
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,

    // Paths
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] },

    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
๐ŸŽฏ

Common Patterns

Builder Pattern

class QueryBuilder<T> {
  private filters: Partial<T> = {};
  private _limit?: number;

  where<K extends keyof T>(key: K, val: T[K]): this {
    this.filters[key] = val;
    return this;
  }

  limit(n: number): this { this._limit = n; return this; }

  build() { return { filters: this.filters, limit: this._limit }; }
}

const q = new QueryBuilder<User>().where("name", "Bob").limit(10).build();

Type-safe Event Emitter

type Events = {
  login: { userId: string };
  logout: { reason: string };
};

class TypedEmitter<T extends Record<string, any>> {
  private listeners = new Map<keyof T, Set<Function>>();

  on<K extends keyof T>(event: K, fn: (data: T[K]) => void) {
    if (!this.listeners.has(event)) this.listeners.set(event, new Set());
    this.listeners.get(event)!.add(fn);
  }

  emit<K extends keyof T>(event: K, data: T[K]) {
    this.listeners.get(event)?.forEach(fn => fn(data));
  }
}

const em = new TypedEmitter<Events>();
em.on("login", d => console.log(d.userId)); // fully typed!

Exhaustive Switch Check

function assertNever(x: never): never {
  throw new Error(`Unexpected: ${x}`);
}

type Action = { type: "inc" } | { type: "dec" } | { type: "reset" };

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case "inc":   return state + 1;
    case "dec":   return state - 1;
    case "reset": return 0;
    default:      return assertNever(action); // compile error if missed
  }
}
๐Ÿ’ก Quick Tips
  • Prefer unknown over any
  • Use as const for literal inference
  • Enable strict: true always
  • Use discriminated unions over type assertions
  • Prefer satisfies for type validation without widening