All Notes

Useful TypeScript Patterns I've Collected

Languages
typescriptpatterns

Discriminated Unions

One of the most powerful patterns in TypeScript. Use a literal field to distinguish between variants:

type ApiState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: Error };

function handleState(state: ApiState<User>) {
  switch (state.status) {
    case "idle":
      return "Waiting...";
    case "loading":
      return "Loading...";
    case "success":
      return `Hello, ${state.data.name}`;
    case "error":
      return `Error: ${state.error.message}`;
  }
}

The Builder Pattern with Types

Use generics and method chaining for type-safe builders:

class QueryBuilder<T extends Record<string, unknown>> {
  private conditions: string[] = [];

  where<K extends keyof T>(field: K, value: T[K]): this {
    this.conditions.push(`${String(field)} = ${value}`);
    return this;
  }

  build(): string {
    return `SELECT * WHERE ${this.conditions.join(" AND ")}`;
  }
}

Branded Types

Prevent mixing up values that have the same underlying type:

type UserId = string & { readonly __brand: "UserId" };
type PostId = string & { readonly __brand: "PostId" };

function getUser(id: UserId) { /* ... */ }
function getPost(id: PostId) { /* ... */ }

// getUser(postId); // Type error!