What if your program could warn you about missing cases before it even runs?
Why Exhaustive checking with never in Typescript? - Purpose & Use Cases
Imagine you have a program that handles different types of user actions, like clicking buttons or submitting forms. You write code to handle each action manually, but as your app grows, you might forget to handle a new action type you added later.
Manually checking every possible action is slow and easy to mess up. You might miss a case, causing bugs that are hard to find. The program might crash or behave unexpectedly because it didn't handle a new action type.
Using exhaustive checking with never in TypeScript helps you catch missing cases at compile time. It forces you to handle every possible type, so your code is safer and more reliable without extra manual work.
switch(action.type) {
case 'click': handleClick(); break;
case 'submit': handleSubmit(); break;
// forgot 'hover' case
}switch(action.type) {
case 'click': handleClick(); break;
case 'submit': handleSubmit(); break;
case 'hover': handleHover(); break;
default: const _exhaustiveCheck: never = action; // error if new type missed
}This concept makes your code future-proof by ensuring you never miss handling a new case, catching errors before your program runs.
When building a menu with different item types, exhaustive checking guarantees you handle every item type, so users never see broken or missing options.
Manual checks can miss new cases and cause bugs.
never helps catch missing cases at compile time.
It 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
