Course outline · 0% complete

0/26 lessons0%

Course overview →

Discriminated Unions

lesson 4-3 · ~13 min · 13/26

One shared tag property

The production-grade pattern for "data that comes in several shapes" is the discriminated union: every shape carries the same literal-typed property, called the tag or discriminant, and you branch on it.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number };

Each member declares kind with a different literal type from lesson 2-3. Checking the tag narrows perfectly:

if (shape.kind === "circle") {
  // shape is the circle member, radius exists
}

You will meet this pattern constantly in real codebases: a web API response that is either data or an error, a user action in a front-end app, a message arriving from another program — each is one union with a tag saying which shape arrived.

An if on the tag works for two members. Past that, the same check reads best as a switch on the tag: switch (shape.kind) compares the tag against each case with === and runs the matching branch, and TypeScript narrows inside each case exactly as it does inside an if.

Code exercise · typescript

Run it. Then try reading shape.radius before the if to confirm the compiler blocks it until the tag is checked.

Code exercise · typescript

The switch version, with a third member added. Each case narrows shape to exactly one member, so the right properties are in reach per branch. Run it, then try reading shape.side inside the "circle" case to see the compiler reject it.

Quiz

What makes a union discriminated?

Code exercise · typescript

Your turn. Define type LoadResult as a discriminated union: { status: "ok"; value: number } or { status: "error"; message: string }. Write describeResult(r: LoadResult): string returning "value is " + value for ok, and "failed: " + message for error. Print the two calls from the starter comments.