BLOGTUTORIAL

10 TypeScript Patterns We Actually Use

MP
Maya PatelFrontend Lead · DevSoc
5 MAR 20256 min read
typescriptweb devpatterns

Forget theoretical type gymnastics. These are the patterns our dev team reaches for every single day when building real production apps.

1. Discriminated Unions for State Machines

When you have a value that can be in one of several mutually exclusive states, reach for a discriminated union rather than a nullable mess. The compiler will tell you when you've forgotten to handle a case.

typescript
type FetchState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; message: string };

// Now TypeScript narrows correctly in each branch
function render<T>(state: FetchState<T>) {
  if (state.status === "success") {
    return state.data; // typed as T ✓
  }
}

2. Const Assertions for Config Objects

When you have a configuration object and you want its values to be literal types rather than broadened primitives, use `as const`. This is especially useful for route definitions and event names.

typescript
const ROUTES = {
  home: "/",
  events: "/events",
  blog: "/blog",
} as const;

type Route = typeof ROUTES[keyof typeof ROUTES];
// Route = "/" | "/events" | "/blog"

3. The satisfies Operator

Introduced in TypeScript 4.9, `satisfies` lets you validate that a value matches a type while keeping the narrowest possible type inference. Use it when you want type safety without losing specificity.

typescript
type NavItem = { href: string; label: string };

const nav = {
  home: { href: "/", label: "Home" },
  blog: { href: "/blog", label: "Blog" },
} satisfies Record<string, NavItem>;

// nav.home.href is still typed as "/" not string ✓

4. Template Literal Types

Template literal types let you construct union types from string patterns. They're great for event names, CSS property keys, and anything else with a predictable naming convention.

typescript
type Direction = "top" | "right" | "bottom" | "left";
type Padding = `padding${Capitalize<Direction>}`;
// "paddingTop" | "paddingRight" | "paddingBottom" | "paddingLeft"

type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"

5. Generic Constraints with keyof

When you write a generic function that operates on object properties, use `keyof` constraints to get type-safe property access. This is the pattern behind most utility types in TypeScript's standard library.

typescript
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  return keys.reduce((acc, key) => {
    acc[key] = obj[key];
    return acc;
  }, {} as Pick<T, K>);
}

const result = pick({ a: 1, b: "hello", c: true }, ["a", "b"]);
// { a: number; b: string } ✓
MP
Maya PatelFrontend Lead · DevSoc

2nd year CS student specialising in frontend. Leads the DevSoc App project team and runs the web development workshop series.

Rotating vector

JOIN US

Privacy policy

Cookie policy

Terms & Conditions

2024 DevSoc