Exhaustive checking helps you catch all possible cases in your code. Using never ensures you don't miss any case, making your program safer and less buggy.
Exhaustive checking with never in Typescript
function assertNever(value: never): never { throw new Error(`Unexpected value: ${value}`); } switch (value) { case 'a': // handle 'a' break; case 'b': // handle 'b' break; default: assertNever(value); // ensures all cases handled }
The assertNever function takes a never type, which means it should never be called if all cases are handled.
If a new case is added to the union type but not handled in the switch, TypeScript will show an error at assertNever.
Fruit but not handled, TypeScript will error at assertNever.type Fruit = 'apple' | 'banana'; function checkFruit(fruit: Fruit) { switch (fruit) { case 'apple': console.log('Apple is red'); break; case 'banana': console.log('Banana is yellow'); break; default: assertNever(fruit); } } function assertNever(x: never): never { throw new Error(`Unexpected value: ${x}`); }
default case uses assertNever to catch any unknown shape kind.type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.size ** 2;
default:
return assertNever(shape);
}
}
function assertNever(x: never): never {
throw new Error(`Unexpected shape: ${JSON.stringify(x)}`);
}This program moves in a direction. The assertNever function ensures all directions are handled. If you add a new direction but forget to handle it, TypeScript will warn you.
type Direction = 'up' | 'down' | 'left' | 'right'; function move(direction: Direction) { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; case 'left': console.log('Moving left'); break; case 'right': console.log('Moving right'); break; default: assertNever(direction); } } function assertNever(x: never): never { throw new Error(`Unknown direction: ${x}`); } move('up'); move('left');
Always use never with a function like assertNever to get compile-time checks.
This technique works best with union types and switch statements.
If you get an error at assertNever, it means you forgot to handle a case.
Exhaustive checking helps catch missing cases in your code.
Using never with a helper function forces you to handle all cases.
This makes your code safer and easier to maintain.
Practice
never in exhaustive checking in TypeScript?Solution
Step 1: Understand the role of
neverin exhaustive checksneveris used to represent values that should never occur, helping catch unhandled cases.Step 2: Connect
By forcing a function to acceptneverusage to exhaustive case handlingnever, TypeScript ensures all cases are covered, or else a compile error occurs.Final Answer:
To ensure all possible cases are handled in a switch or conditional -> Option DQuick Check:
Exhaustive checking = catch all cases [OK]
- Thinking never allows any value
- Using never to ignore errors
- Confusing never with unknown type
never to enforce exhaustive checking?Solution
Step 1: Identify correct parameter and return types
The helper function must accept a parameter of typeneverand returnneverbecause it throws an error.Step 2: Check function body matches expected behavior
The function throws an error with the unexpected value, which is the standard pattern for exhaustive checks.Final Answer:
function assertNever(x: never): never { throw new Error('Unexpected value: ' + x); } -> Option AQuick Check:
Helper function with never param and throw = correct [OK]
- Using any or unknown instead of never
- Not throwing an error inside the function
- Returning void instead of never
type Shape = 'circle' | 'square';
function area(shape: Shape) {
switch (shape) {
case 'circle':
return 3.14 * 2 * 2;
// Missing case for 'square'
default:
return assertNever(shape);
}
}
function assertNever(x: never): never {
throw new Error('Unexpected shape: ' + x);
}
console.log(area('square'));Solution
Step 1: Analyze switch cases and input
The function handles 'circle' but not 'square', so at runtime, calling area('square') hits the default case.Step 2: Understand assertNever behavior at runtime
The assertNever function throws an error with the unexpected shape, so the program throws a runtime error.Final Answer:
Throws Error: Unexpected shape: square -> Option BQuick Check:
Missing case causes runtime error when called [OK]
- Assuming missing case returns undefined
- Thinking it returns a number for all inputs
- Expecting compile error instead of runtime error
type Direction = 'up' | 'down' | 'left';
function move(dir: Direction) {
switch (dir) {
case 'up':
return 'Moving up';
case 'down':
return 'Moving down';
default:
return assertNever(dir);
}
}
function assertNever(x: never): never {
throw new Error('Unexpected direction: ' + x);
}Solution
Step 1: Check all union cases in switch
The type Direction has 'up', 'down', and 'left', but 'left' is missing in the switch.Step 2: Understand impact of missing case with assertNever
Missing 'left' causes default to call assertNever with a string, which is not never, causing a compile error.Final Answer:
Missing case for 'left' in switch statement -> Option AQuick Check:
All union cases must be handled [OK]
- Ignoring missing union cases
- Thinking default case covers all safely
- Wrong assertNever parameter type
Status values?type Status = 'success' | 'error' | 'loading';
function handleStatus(status: Status) {
switch (status) {
case 'success':
return 'Done';
case 'error':
return 'Failed';
// Missing 'loading' case
default:
return assertNever(status);
}
}
function assertNever(x: never): never {
throw new Error('Unexpected status: ' + x);
}Solution
Step 1: Identify missing union case in switch
The 'loading' case is missing, so the switch is not exhaustive.Step 2: Add missing case to ensure all Status values are handled
Addingcase 'loading':with appropriate return ensures exhaustive checking and no runtime errors.Final Answer:
Add a case for 'loading' in the switch statement -> Option CQuick Check:
All union members must have cases [OK]
- Removing default case without adding missing cases
- Changing type to avoid handling all cases
- Using any instead of never in assertNever
Ready to prove it?
Pass the test and this pattern moves unknown
