TypeScript Tutorial · Generics and Type Manipulation · Lesson 20 of 25

TypeScript Discriminated Unions

Model states safely with a shared literal tag.

A union with a tag

Give each member of a union a common literal property (the discriminant, often kind, type or status). Checking it narrows to exactly one member:

Example 1Try it yourself
type Event =
  | { type: "click"; x: number; y: number }
  | { type: "keypress"; key: string }
  | { type: "scroll"; offset: number };

function describe(e: Event): string {
  switch (e.type) {
    case "click":
      return `click at ${e.x},${e.y}`;
    case "keypress":
      return `key ${e.key}`;
    case "scroll":
      return `scrolled to ${e.offset}`;
  }
}
const events: Event[] = [{ type: "click", x: 10, y: 20 }, { type: "keypress", key: "Enter" }, { type: "scroll", offset: 300 }];
events.forEach((e) => console.log(describe(e)));

Output

click at 10,20
key Enter
scrolled to 300

Modelling state

Discriminated unions make impossible states impossible — a request can't have both data and error:

Example 2Try it yourself
type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: string };

function render(state: RequestState<string[]>): string {
  if (state.status === "success") return `Loaded ${state.data.length} items`;
  if (state.status === "error") return `Error: ${state.error}`;
  return state.status === "loading" ? "Loading..." : "Press load";
}
console.log(render({ status: "idle" }));
console.log(render({ status: "loading" }));
console.log(render({ status: "success", data: ["a", "b"] }));
console.log(render({ status: "error", error: "Network down" }));

Output

Press load
Loading...
Loaded 2 items
Error: Network down

The Result type is a popular pattern for errors without exceptions — try the Result type problem.

Test yourself

What makes a union 'discriminated'?

  1. Using the | operator
  2. A shared literal property that identifies each member
  3. Using classes
  4. Having exactly two members
Show answer

Answer: A shared literal property that identifies each member

Practice next