Skip to content
Unverified — AI-generated content. Help verify this page

TypeScript Cheat Sheet

Quick reference for TypeScript utility types, generics, type guards, patterns, and common gotchas.


Utility Types

Built-in Utility Types

TypeDescriptionExample
Partial<T>All properties optionalPartial<User>
Required<T>All properties requiredRequired<Config>
Readonly<T>All properties readonlyReadonly<State>
Pick<T, K>Select propertiesPick<User, 'id' | 'name'>
Omit<T, K>Remove propertiesOmit<User, 'password'>
Record<K, V>Object with typed keys/valuesRecord<string, number>
Extract<T, U>Extract union membersExtract<'a' | 'b' | 'c', 'a' | 'b'>
Exclude<T, U>Remove union membersExclude<'a' | 'b' | 'c', 'a'>
NonNullable<T>Remove null and undefinedNonNullable<string | null>
ReturnType<T>Function return typeReturnType<typeof fn>
Parameters<T>Function parameter typesParameters<typeof fn>
Awaited<T>Unwrap Promise typeAwaited<Promise<string>>
InstanceType<T>Class instance typeInstanceType<typeof MyClass>
ConstructorParameters<T>Constructor param typesConstructorParameters<typeof MyClass>

Usage Examples

typescript
interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

// Partial - for update payloads
function updateUser(id: number, data: Partial<User>) {}
updateUser(1, { name: 'Alice' }); // Only need some fields

// Pick - for specific views
type UserPreview = Pick<User, 'id' | 'name'>;

// Omit - for creation (no ID yet)
type CreateUser = Omit<User, 'id'>;

// Record - for lookup maps
type RolePermissions = Record<User['role'], string[]>;
const permissions: RolePermissions = {
  admin: ['read', 'write', 'delete'],
  user: ['read'],
};

Generics

Basic Generics

typescript
// Generic function
function identity<T>(value: T): T {
  return value;
}

// Generic with constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// Generic interface
interface Repository<T> {
  findById(id: string): Promise<T | null>;
  save(entity: T): Promise<T>;
  delete(id: string): Promise<void>;
}

// Generic class
class TypedMap<K, V> {
  private map = new Map<K, V>();
  set(key: K, value: V): void { this.map.set(key, value); }
  get(key: K): V | undefined { return this.map.get(key); }
}

Generic Constraints

typescript
// Constrain to objects with id
function findById<T extends { id: string }>(items: T[], id: string): T | undefined {
  return items.find(item => item.id === id);
}

// Constrain to specific keys
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  const result = {} as Pick<T, K>;
  keys.forEach(key => { result[key] = obj[key]; });
  return result;
}

// Default generic type
interface ApiResponse<T = unknown> {
  data: T;
  status: number;
  message: string;
}

Advanced Generic Patterns

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

// Infer keyword
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type ArrayElement<T> = T extends (infer U)[] ? U : never;

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

// Template literal types
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // 'onClick'

Type Guards

typeof Guards

typescript
function process(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows value is string here
    return value.toUpperCase();
  }
  // TypeScript knows value is number here
  return value.toFixed(2);
}

instanceof Guards

typescript
class ApiError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
  }
}

function handleError(error: Error) {
  if (error instanceof ApiError) {
    // TypeScript knows error is ApiError
    console.log(error.statusCode);
  }
}

Custom Type Guards

typescript
interface Cat { meow(): void; }
interface Dog { bark(): void; }

// Type predicate function
function isCat(animal: Cat | Dog): animal is Cat {
  return 'meow' in animal;
}

function handleAnimal(animal: Cat | Dog) {
  if (isCat(animal)) {
    animal.meow(); // TypeScript knows it's a Cat
  } else {
    animal.bark(); // TypeScript knows it's a Dog
  }
}

Discriminated Unions

typescript
// The "type" field is the discriminant
type Shape =
  | { type: 'circle'; radius: number }
  | { type: 'rectangle'; width: number; height: number }
  | { type: 'triangle'; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.type) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'rectangle':
      return shape.width * shape.height;
    case 'triangle':
      return (shape.base * shape.height) / 2;
  }
}

// Exhaustive check helper
function assertNever(x: never): never {
  throw new Error(`Unexpected value: ${x}`);
}

Assertion Functions

typescript
function assertDefined<T>(value: T | null | undefined, msg?: string): asserts value is T {
  if (value === null || value === undefined) {
    throw new Error(msg ?? 'Value is not defined');
  }
}

function process(input: string | null) {
  assertDefined(input, 'Input required');
  // TypeScript knows input is string here
  console.log(input.toUpperCase());
}

Common Patterns

Builder Pattern

typescript
class QueryBuilder<T> {
  private filters: string[] = [];
  private sortField?: keyof T;

  where(field: keyof T, value: unknown): this {
    this.filters.push(`${String(field)} = ${value}`);
    return this;
  }

  orderBy(field: keyof T): this {
    this.sortField = field;
    return this;
  }

  build(): string {
    let query = `SELECT * FROM table`;
    if (this.filters.length) {
      query += ` WHERE ${this.filters.join(' AND ')}`;
    }
    if (this.sortField) {
      query += ` ORDER BY ${String(this.sortField)}`;
    }
    return query;
  }
}

Result Type (Error Handling)

typescript
type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

function ok<T>(value: T): Result<T, never> {
  return { ok: true, value };
}

function err<E>(error: E): Result<never, E> {
  return { ok: false, error };
}

// Usage
function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return err('Division by zero');
  return ok(a / b);
}

const result = divide(10, 0);
if (result.ok) {
  console.log(result.value); // TypeScript knows it's number
} else {
  console.log(result.error); // TypeScript knows it's string
}

Branded Types

typescript
// Prevent mixing up primitive types
type UserId = string & { readonly __brand: unique symbol };
type OrderId = string & { readonly __brand: unique symbol };

function createUserId(id: string): UserId {
  return id as UserId;
}

function getUser(id: UserId): void {}
function getOrder(id: OrderId): void {}

const userId = createUserId('u123');
getUser(userId);  // OK
// getOrder(userId); // Error: UserId is not assignable to OrderId

Strict Event Emitter

typescript
type EventMap = {
  'user:login': { userId: string; timestamp: number };
  'user:logout': { userId: string };
  'error': { message: string; code: number };
};

class TypedEmitter<T extends Record<string, unknown>> {
  private handlers = new Map<string, Set<Function>>();

  on<K extends keyof T>(event: K, handler: (payload: T[K]) => void): void {
    if (!this.handlers.has(event as string)) {
      this.handlers.set(event as string, new Set());
    }
    this.handlers.get(event as string)!.add(handler);
  }

  emit<K extends keyof T>(event: K, payload: T[K]): void {
    this.handlers.get(event as string)?.forEach(h => h(payload));
  }
}

const emitter = new TypedEmitter<EventMap>();
emitter.on('user:login', (data) => {
  // data is typed as { userId: string; timestamp: number }
  console.log(data.userId);
});

Type Narrowing Techniques

TechniqueSyntaxNarrows To
typeoftypeof x === 'string'Primitive types
instanceofx instanceof DateClass instances
in'name' in xObjects with property
Discriminantx.type === 'a'Union members
Truthinessif (x)Non-nullish
Equalityx === nullSpecific value
Custom guardisFoo(x)Custom type
AssertionassertFoo(x)Custom type (throws)

Common Gotchas

Object vs object vs Record

typescript
// Object - almost anything (avoid)
let a: Object = 'string'; // works but useless

// object - any non-primitive
let b: object = {}; // OK
// let c: object = 'string'; // Error

// Record<string, unknown> - typed object (preferred)
let d: Record<string, unknown> = { key: 'value' };

Readonly Arrays

typescript
// Mutable array
const arr: number[] = [1, 2, 3];
arr.push(4); // OK

// Readonly array
const readonlyArr: readonly number[] = [1, 2, 3];
// readonlyArr.push(4); // Error

// as const makes deeply readonly
const config = {
  ports: [3000, 3001],
  host: 'localhost',
} as const;
// config.ports.push(3002); // Error
// config.host = 'other';   // Error

Enums vs Union Types

typescript
// Prefer union types over enums
// Bad: enum creates runtime code
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
}

// Good: union type is zero-runtime
type Direction = 'UP' | 'DOWN';

// If you need enum-like objects, use as const
const Direction = {
  Up: 'UP',
  Down: 'DOWN',
} as const;
type Direction = typeof Direction[keyof typeof Direction];

Function Overloads

typescript
// Overload signatures
function parse(input: string): number;
function parse(input: string[]): number[];
// Implementation signature
function parse(input: string | string[]): number | number[] {
  if (Array.isArray(input)) {
    return input.map(Number);
  }
  return Number(input);
}

parse('42');     // returns number
parse(['1','2']); // returns number[]

The any Escape Hatches

typescript
// unknown is the safe alternative to any
function process(data: unknown) {
  // Must narrow before using
  if (typeof data === 'string') {
    console.log(data.toUpperCase());
  }
}

// never for exhaustive checks
type Shape = 'circle' | 'square';
function handle(shape: Shape) {
  switch (shape) {
    case 'circle': return;
    case 'square': return;
    default:
      const _exhaustive: never = shape;
      throw new Error(`Unhandled: ${_exhaustive}`);
  }
}

tsconfig.json Key Options

OptionRecommendedWhy
stricttrueEnables all strict checks
noUncheckedIndexedAccesstrueArray/object access returns T | undefined
exactOptionalPropertyTypestrueDistinguishes undefined from missing
noImplicitReturnstrueAll code paths must return
noFallthroughCasesInSwitchtruePrevent switch fallthrough
forceConsistentCasingInFileNamestruePrevent casing bugs on case-insensitive OS
skipLibChecktrueFaster builds, skip .d.ts checking
moduleResolutionbundlerModern bundler resolution
targetES2022Modern JS output
verbatimModuleSyntaxtrueExplicit type imports

When to Use X vs Y

DecisionChoice AChoice BUse A WhenUse B When
TypeinterfacetypeObject shapes, extendsUnions, intersections, mapped types
Assertionas TypeType guardYou are certain, one-offRuntime check needed, reusable
Null check! (non-null assertion)Proper null check100% certain, testingProduction code
Generic defaultT = unknownNo defaultLibrary API, optional genericInternal code
Importimport typeimportType only, no runtimeNeed the value at runtime

Test Yourself
  1. What utility type makes all properties of a type optional?Partial<T>

  2. How do you create a type that picks only the id and name properties from User?Pick<User, 'id' | 'name'>

  3. What keyword extracts the inner type from a Promise<T> in a conditional type?infer (e.g., T extends Promise<infer U> ? U : T)

  4. How do you write a custom type guard function that checks if a value is a Cat?function isCat(animal: Cat | Dog): animal is Cat

  5. What is the zero-runtime alternative to TypeScript enums? Union types: type Direction = 'UP' | 'DOWN'

  6. What tsconfig option makes array/object indexing return T | undefined?noUncheckedIndexedAccess

  7. How do you get the return type of a function?ReturnType<typeof fn>

  8. What type should you use instead of any for safe unknown data?unknown -- it forces you to narrow the type before using it.

  9. What is a branded type used for? Preventing accidental mixing of primitive types that are structurally identical (e.g., UserId vs OrderId).

  10. What is the difference between interface and type in TypeScript?interface is best for object shapes and extends; type is best for unions, intersections, and mapped types.

Common Gotchas

  • Using any instead of unknown. any disables all type checking. Use unknown and narrow with type guards.
  • Non-null assertion ! in production code. It tells the compiler "trust me, this is not null" -- but at runtime it can absolutely be null. Use proper null checks.
  • Enums generate runtime code. Prefer union types (type Status = 'active' | 'inactive') for zero-runtime overhead.
  • Forgetting as const for literal types. Without it, const arr = [1, 2, 3] is typed as number[], not readonly [1, 2, 3].

One-Liner Summary

TypeScript adds a compile-time type system to JavaScript -- master utility types, generics, discriminated unions, and type guards to catch bugs before they reach production.

"What I cannot create, I do not understand." — Richard Feynman