assertNever Exhaustiveness Checking

Last updated: 2026-10-22

assertNever Exhaustiveness Checking

Exhaustiveness checking is compile-time insurance that every member of a union has been handled. The core is the assertNever trick: in the switch's default branch, type the parameter as never — if a member is missed, it can still flow in, the parameter is no longer never, and the compiler errors immediately.

GroupNarrowing & Assertions
KindSyntax
Strict modeNot required

ℹ️ Info The editor transpiles TypeScript to JavaScript and runs it (no type checking); see each example's Output for type-check results.

📝 Syntax

function assertNever(x: never): never {
  throw new Error("Unhandled: " + JSON.stringify(x));
}

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "square": return shape.side ** 2;
    default:
      return assertNever(shape); // compile error if a branch is missed
  }
}

⚙️ Parameters

x: nevernever is a subtype of every type with no inhabitants — the argument satisfies never only when every branch is handled and the leftover type is empty

Returns:assertNever returns never (it never returns normally; it throws at runtime); the compile-time payoff is the error "Argument of type 'X' is not assignable to parameter of type 'never'".

▶ Example

Edit the code below and click Try it to see the transpiled result:

Output:

3.14
9

(Runtime output after transpilation: all branches are present, so assertNever is never reached. Adding kind: "triangle" to Shape without a branch makes tsc report "Argument of type '{ kind: "triangle"; ... }' is not assignable to parameter of type 'never'" on the default line)

💡 Notes

ℹ️ Info How it works: TS removes handled members from the union inside a switch. Once all are handled, shape is never and assigns to the never parameter; miss one and the assignment fails, with the error pointing at the default line.
💡 Tip The biggest payoff comes when adding a new union member: no grep needed — the compiler lists every switch that now misses a branch.
⚠️ Note Exhaustiveness depends on discriminated unions with literal discriminants. Switching on a string-typed field or using an if-else chain prevents the compiler from judging exhaustiveness.
More cases coming soon.

📚 Related tutorials

❓ FAQ

Q Can't I just write a complete switch without assertNever?
A noImplicitReturns/strict can catch missing return values, but not a logically unhandled case (e.g. a branch silently returning a default). assertNever turns "missed case" into a hard compile error.
Q Does exhaustiveness work with if-else chains?
A Yes — call assertNever(x) in the final else; the mechanism is identical. Still, switch reads better and plays nicest with discriminant analysis, so prefer it.
Q Is assertNever ever executed at runtime?
A Not if the code is maintained correctly — it only runs for the missed-branch scenario the compiler already prevents. Keeping the throw is defensive: if runtime data contradicts the declared types (e.g. stale cached data), it surfaces early instead of silently taking the wrong branch.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏