Skip to content
isam.chajia
Engineering

TypeScript patterns that catch bugs before runtime

Discriminated unions, exhaustive checks and branded types turn whole classes of runtime errors into compile-time ones.

Isam Chajia6 min read
Stacked index cards and an open book next to a laptop

Types are most valuable when they make an invalid state impossible to write. A few small patterns do most of that work.

Model states as a union

Instead of a bag of booleans, describe each state exactly once. TypeScript then refuses to let you read fields that do not exist in that state.

type RequestState =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: string[] }
  | { status: "error"; message: string };

Ask the compiler for exhaustiveness

A `never` check turns a forgotten branch into a build error rather than a mysterious runtime bug.

function assertNever(value: never): never {
  throw new Error(`Unhandled case: ${JSON.stringify(value)}`);
}

Prefer one source of truth

  • Derive related values instead of storing them twice.
  • Keep optional fields truly optional, not empty strings.
  • Use narrow types at the boundaries, then trust them inside.

These patterns cost almost nothing at runtime and remove bugs that are otherwise found in production.

  • TypeScript
  • JavaScript
  • Best Practices
Engineering8 min read

Designing REST APIs that age well

Resource naming, status codes, pagination and versioning — the decisions that keep an API predictable years after launch.

Available for new work

Enjoying the writing?

If an article sparked an idea for your product or team, I'd be glad to hear about it.

Replies within 2 working daysMarrakech, Morocco