Codectionary / Developer documentation / TypeScript

Exhaustive Discriminated Unions

A discriminated union uses a shared literal-valued field to identify each variant. Checking that field narrows the associated payload. A never assignment after all variants provides an exhaustiveness check: adding a variant requires updating the handling code.

Syntax

switch (value.kind) { /* cases */ }
const unhandled: never = value;

Examples

Exhaustive Discriminated Unions: working example

Run this standalone example after compiling with TypeScript 5.4+ in strict mode, targeting ES2022. Expected result: The program prints Value: 42; adding an unhandled union member causes a type error at the never assignment.

type Result =
  | { kind: "success"; value: number }
  | { kind: "failure"; message: string };
function describe(result: Result): string {
  switch (result.kind) {
    case "success": return `Value: ${result.value}`;
    case "failure": return `Error: ${result.message}`;
    default: {
      const unhandled: never = result;
      throw new Error(`Unexpected result: ${unhandled}`);
    }
  }
}
console.log(describe({ kind: "success", value: 42 }));
  1. Expected result: The program prints Value: 42; adding an unhandled union member causes a type error at the never assignment.
  2. Each branch exposes only its own variant’s payload.
  3. The throwing default also fails loudly if unvalidated JavaScript supplies an impossible value.

Best practices

  • Model distinct states explicitly instead of combining unrelated optional properties.
  • Validate external inputs before treating them as a discriminated union.

Runtime & versions

Compiled to JavaScript. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

TypeScript Handbook

Specifications & further reading