Skip to main content

Exhaustive check through never

Short answer: make a discriminated union and add a never-based "guard" to the switch. Then, when a new variant is added, TypeScript will raise a compile error.


The basic technique (exhaustive check through never)

javascript
type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "square": return s.side ** 2; case "triangle": return (s.base * s.height) / 2; default: // if a new variant appears, s will NOT be 'never' here → error const _exhaustive: never = s; throw new Error(`Unhandled case: ${_exhaustive}`); } }
  • When you add, for example, { kind: "rectangle" }, s in default stops being never → a compile error forces you to add the case.

A variant with a helper function assertNever

Convenient if you do this check often:

javascript
function assertNever(x: never): never { throw new Error(`Unhandled case: ${String(x)}`); } function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "square": return s.side ** 2; case "triangle": return (s.base * s.height) / 2; default: return assertNever(s); // a type error fires here on a new variant } }

Without default: a "post-switch" guard

If you don't want a default, you can require every branch to return a value, and then check never after the switch:

javascript
function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "square": return s.side ** 2; case "triangle": return (s.base * s.height) / 2; } const _exhaustive: never = s; // fires when a new kind is added return _exhaustive; }

Useful settings and linting

  • The ESLint rule @typescript-eslint/switch-exhaustiveness-check highlights unclosed switch statements.
  • The compiler flag noFallthroughCasesInSwitch helps avoid fallthrough between cases (not about exhaustiveness, but useful).

Summary

To guarantee that a switch handles all variants of a union:

  1. Use a discriminant field (for example, kind).
  2. Add an exhaustive check by assigning to never (in default or after the switch).
  3. (Optionally) Extract the check into assertNever.

Short Answer

Interview ready
Premium

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