TypeScript Cheat Sheet

Intermediate | 15 min read | 2025.01.10

Basic Types

// Primitive types
const name: string = "Alice";
const age: number = 25;
const isActive: boolean = true;
const id: bigint = 100n;
const symbol: symbol = Symbol("id");

// Arrays
const numbers: number[] = [1, 2, 3];
const names: Array<string> = ["Alice", "Bob"];

// Tuples
const tuple: [string, number] = ["Alice", 25];
const namedTuple: [name: string, age: number] = ["Alice", 25];

// Objects
const user: { name: string; age: number } = { name: "Alice", age: 25 };

// any, unknown, never
let anyValue: any = "anything";
let unknownValue: unknown = "must check type";
function throwError(): never {
  throw new Error("error");
}

Type Aliases and Interfaces

// Type alias
type User = {
  id: string;
  name: string;
  email: string;
};

type ID = string | number;

// Interface
interface Product {
  id: string;
  name: string;
  price: number;
}

// Extension
interface AdminUser extends User {
  role: "admin";
  permissions: string[];
}

type ExtendedProduct = Product & {
  category: string;
};

Union Types and Intersection Types

// Union type (either)
type Status = "pending" | "approved" | "rejected";
type StringOrNumber = string | number;

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

// Discriminated union
type Result<T> =
  | { success: true; data: T }
  | { success: false; error: string };

function handleResult(result: Result<string>) {
  if (result.success) {
    console.log(result.data); // string
  } else {
    console.log(result.error); // string
  }
}

Generics

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

// Multiple type parameters
function pair<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}

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

// Interfaces
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// Classes
class Container<T> {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }
}

Utility Types

interface User {
  id: string;
  name: string;
  email: string;
  age?: number;
}

// Partial - Make all properties optional
type PartialUser = Partial<User>;
// { id?: string; name?: string; email?: string; age?: number; }

// Required - Make all properties required
type RequiredUser = Required<User>;

// Readonly - Make all properties read-only
type ReadonlyUser = Readonly<User>;

// Pick - Select specific properties
type UserName = Pick<User, "id" | "name">;
// { id: string; name: string; }

// Omit - Exclude specific properties
type UserWithoutEmail = Omit<User, "email">;

// Record - Specify key and value types
type UserRecord = Record<string, User>;

// Exclude - Exclude from union
type Status = "pending" | "approved" | "rejected";
type ActiveStatus = Exclude<Status, "rejected">;
// "pending" | "approved"

// Extract - Extract from union
type ExtractedStatus = Extract<Status, "pending" | "approved">;

// NonNullable - Exclude null and undefined
type NonNullableString = NonNullable<string | null | undefined>;

// ReturnType - Get function return type
function getUser() {
  return { id: "1", name: "Alice" };
}
type UserReturn = ReturnType<typeof getUser>;

// Parameters - Get function parameter types
type GetUserParams = Parameters<typeof getUser>;

Type Guards

// typeof
function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  return value * 2;
}

// instanceof
function handleError(error: Error | string) {
  if (error instanceof Error) {
    return error.message;
  }
  return error;
}

// in operator
interface Dog {
  bark(): void;
}
interface Cat {
  meow(): void;
}

function speak(animal: Dog | Cat) {
  if ("bark" in animal) {
    animal.bark();
  } else {
    animal.meow();
  }
}

// Custom type guard
function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  );
}

Conditional Types

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

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

// Array element type
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type Item = ArrayElement<string[]>; // string

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

Mapped Types

// Basic
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

// Modifier manipulation
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

type OptionalToRequired<T> = {
  [K in keyof T]-?: T[K];
};

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

type UserGetters = Getters<User>;
// { getId: () => string; getName: () => string; ... }

Template Literal Types

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = "/users" | "/products";

// Combinations
type ApiRoute = `${HttpMethod} ${Endpoint}`;
// "GET /users" | "GET /products" | "POST /users" | ...

// Case conversion
type UpperCase = Uppercase<"hello">; // "HELLO"
type LowerCase = Lowercase<"HELLO">; // "hello"
type Capitalize = Capitalize<"hello">; // "Hello"
type Uncapitalize = Uncapitalize<"Hello">; // "hello"

Common Patterns

// Nullable type
type Nullable<T> = T | null;
type Optional<T> = T | undefined;
type Maybe<T> = T | null | undefined;

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

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

// Readonly array
type ReadonlyArray<T> = readonly T[];

// Async function return type
type AsyncReturnType<T extends (...args: any) => Promise<any>> =
  T extends (...args: any) => Promise<infer R> ? R : never;

Practical Tips

// as const for literal types
const colors = ["red", "green", "blue"] as const;
type Color = typeof colors[number]; // "red" | "green" | "blue"

// satisfies for type checking (while preserving inference)
const config = {
  port: 3000,
  host: "localhost",
} satisfies Record<string, string | number>;

// Union type from object
const statusMap = {
  pending: "Pending",
  approved: "Approved",
  rejected: "Rejected",
} as const;
type Status = keyof typeof statusMap; // "pending" | "approved" | "rejected"
← Back to list