← Back to TypeScript Course | Chapter 5: Type Aliases and Union Types | Lesson 5 of 9

Discriminated Unions

A discriminated union is a union of object types that share a common property with different literal values. That property, called the discriminant, helps TypeScript identify the exact object type safely.

Creating a Discriminated Union

Each member of a discriminated union shares one common property, conventionally named type or kind, but each member gives it a different literal value. TypeScript uses that shared discriminant property to figure out exactly which specific member of the union it's dealing with.

Example: Creating a Discriminated Union

typescript
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Shape = Circle | Square;
const shape: Shape = { kind: "circle", radius: 5 };
console.log(shape.kind);

Narrowing with the Discriminant

Checking the value of the discriminant property, usually with an if statement or switch, narrows the entire object down to the corresponding union member. Inside that narrowed branch, TypeScript allows access to properties that belong specifically to that one member, not the whole union.

Example: Narrowing with the Discriminant

typescript
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
function area(shape: Circle | Square): number {
  if (shape.kind === "circle") return 3.14 * shape.radius ** 2;
  return shape.side ** 2;
}
console.log(area({ kind: "square", side: 4 }));

Discriminated Unions for Actions

Discriminated unions are especially well-suited to representing distinct actions in an application, like a Redux-style reducer, where each action carries its own type value and only the data fields relevant to that specific action. This keeps action handling both type-safe and easy to follow.

Example: Discriminated Unions for Actions

typescript
type Increment = { type: "increment"; amount: number };
type Reset = { type: "reset" };
function reducer(action: Increment | Reset, state: number): number {
  if (action.type === "increment") return state + action.amount;
  return 0;
}
console.log(reducer({ type: "increment", amount: 5 }, 10));

Multiple Discriminated Members

A discriminated union can contain as many members as needed, and each one should use its own unique literal value for the discriminant property. As long as every value is unique, TypeScript can reliably distinguish between any number of possible cases.

Example: Multiple Discriminated Members

typescript
type A = { kind: "a"; value: number };
type B = { kind: "b"; text: string };
type C = { kind: "c" };
function handle(x: A | B | C) {
  console.log(x.kind);
}
handle({ kind: "b", text: "hi" });

Discriminated Unions and Exhaustive Handling

When every member of a discriminated union has been explicitly handled, TypeScript can help confirm nothing was missed using an exhaustiveness check, often implemented with a helper function whose parameter is typed as never. If a new member is added to the union later and left unhandled, this pattern surfaces it as a compile error instead of a silent runtime bug.

Example: Discriminated Unions and Exhaustive Handling

typescript
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
function area(shape: Circle | Square): number {
  switch (shape.kind) {
    case "circle": return 3.14 * shape.radius ** 2;
    case "square": return shape.side ** 2;
    default:
      const exhaustive: never = shape;
      return exhaustive;
  }
}
console.log(area({ kind: "circle", radius: 2 }));
🔒

Chapter Quiz — Complete all 9 topics to unlock

0/9 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.