Skip to main content

What is a "discriminated union"?

What is a discriminated union?

A discriminated union is a union of objects that share a common tag property (called the discriminant or tag), whose value uniquely determines the object's type.

This lets TypeScript recognize which exact variant of the type is being used, and automatically narrow the type (type narrowing) during checks.


A simple example

javascript
type Circle = { kind: "circle"; radius: number; }; type Square = { kind: "square"; side: number; }; type Shape = Circle | Square;

Here:

  • Shape is a union (Circle | Square);
  • the kind field is the discriminant, it tells the variants apart.

Usage with type narrowing

javascript
function area(shape: Shape): number { if (shape.kind === "circle") { // here shape: Circle return Math.PI * shape.radius ** 2; } else { // here shape: Square return shape.side ** 2; } }

TypeScript automatically determines that in each if/else block the type has narrowed to the correct variant (Circle or Square).


How this works

TypeScript sees:

  1. a union (Circle | Square),
  2. a common property (kind),
  3. and unique string literals in that property.

After the check shape.kind === "circle", the compiler knows that in this context shape is definitely Circle.


An example with several variants

javascript
type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function getArea(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "triangle": return (shape.base * shape.height) / 2; default: const _exhaustive: never = shape; // an exhaustiveness check return _exhaustive; } }

Here:

  • switch automatically narrows the type by kind.
  • never in default guarantees that all variants are handled.

Advantages of a discriminated union

1. Safe branching - TS guarantees that you have handled all cases. 2. No as and no casts - the type is determined automatically. 3. Works great with switch, if, and pattern-like checks. 4. Well suited for describing states, actions, errors, and events.


Real-world examples

1. Loading states (state machine pattern)

javascript
type LoadingState = { status: "loading" }; type SuccessState = { status: "success"; data: string }; type ErrorState = { status: "error"; error: Error }; type RequestState = LoadingState | SuccessState | ErrorState; function render(state: RequestState) { switch (state.status) { case "loading": return "Loading..."; case "success": return `Data: ${state.data}`; case "error": return `Error: ${state.error.message}`; } }

2. Redux-like actions

javascript
type Action = | { type: "add"; payload: number } | { type: "remove"; id: string } | { type: "reset" }; function reducer(state: number[], action: Action) { switch (action.type) { case "add": return [...state, action.payload]; case "remove": return state.filter((_, i) => i.toString() !== action.id); case "reset": return []; } }

3. API responses

javascript
type ApiResponse<T> = | { kind: "ok"; data: T } | { kind: "error"; message: string }; function handleResponse(res: ApiResponse<string>) { if (res.kind === "ok") { console.log("OK:", res.data); } else { console.error("Error:", res.message); } }

The difference from a regular union

Regular unionDiscriminated union
type A = {a: number} | {b: string}type A = { kind: "x"; a: number } | { kind: "y"; b: string }
When checking properties, TypeScript does not know which variant it isTypeScript automatically narrows the type by kind
Checks must be done manually ('a' in obj)Comparing the tag is enough (obj.kind === 'x')

Summary

ConceptDescription
Discriminant (tag)A common property with unique literal values that determines the type variant
Discriminated unionA union of types distinguished by a tag
Main advantageTypeScript narrows the type itself, based on the tag's value
Typical useLoading states, actions, result variants, finite state machines

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.