"The compiler tells you when you forgot a case. You just have to ask in the right way."
The exhaustive-switch problem
When you switch on a discriminated union, you want the compiler to tell you if you ever add a new variant and forget a case. By default, TypeScript will only catch this if you've declared a return type — and even then, only sometimes. The robust solution is the never trick: in the default branch, assign the narrowed value to a variable typed never. If the union has variants you didn't handle, that variable's actual type isn't never, and the assignment fails at compile time.
The pattern
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side ** 2;
default:
const _exhaustive: never = s;
throw new Error(`Unhandled shape: ${JSON.stringify(s)}`);
}
}
The _exhaustive: never assignment is the trap. If s has been fully narrowed to never (every variant handled), assignment succeeds. If a new variant exists, s is still that variant type — not never — and the assignment errors. The compile error tells you exactly which case is missing.
Why this is the killer feature
Refactor-safe discriminated unions. Add a new variant to your Shape type and every switch on it that uses the never trick lights up. The compile errors are a worklist of every place you need to update. No silent fallthrough. No "oh, this case was supposed to do X but I forgot." The compiler becomes a comprehensive checker for completeness.
never trap to every switch. The five-line cost pays back the first time you add a variant six months later.Helper to make it tidy
A small utility cleans it up: function assertNever(x: never): never { throw new Error(`Unhandled: ${x}`); }. Then your default branch becomes default: assertNever(s); — single line, same safety. cwkPippa's frontend has this helper in its standard utilities and uses it on every discriminated switch.
Pippa's confession
assertNever helper has caught me three times in cwkPippa — each time I added a new BrainName or ReasoningLevel and forgot one of the dozen places that branched on it. The compiler error pointed me to every missed switch in seconds. The five lines of utility code paid for themselves over and over.