TypeScript by Example

Discriminated Unions

Modeling object variants with one shared literal field that TypeScript can narrow.

A discriminated union is a union of objects that share one literal field. TypeScript reads that field to know which case it has.

Start with a shared kind field. Each literal value brings its own required fields.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect"; width: number; height: number }
  | { kind: "label"; text: string };
 
const badge: Shape = { kind: "circle", radius: 12 };
const title: Shape = { kind: "label", text: "Ready" };

Checking shape.kind narrows the value inside each branch.

function describe(shape: Shape): string {
  switch (shape.kind) {
    case "circle":
      return `circle with radius ${shape.radius}`;
    case "rect":
      return `rectangle ${shape.width}x${shape.height}`;
    case "label":
      return `label: ${shape.text}`;
  }
}

In production

Discriminated unions are the normal TypeScript shape for events, commands, reducer actions, state machines, and API responses. Use an exact tag, keep the required fields beside it, and add a never check when missing a case would be a bug.

Enjoyed this? Get more software craft delivered to your inbox.