Careeroza
Exhaustive Switch
Two Ways to Enforce Exhaustiveness
You saw assertNever in the medium level. Another way is to declare an explicit return type and turn on noImplicitReturns, or use satisfies never. And a Record lookup table is exhaustive by construction.
Example
type Plan = "free" | "pro" | "enterprise";
function seats(plan: Plan): number {
switch (plan) {
case "free": return 1;
case "pro": return 10;
case "enterprise": return 500;
default:
plan satisfies never;
throw new Error("unreachable");
}
}
const monthlyPrice: Record<Plan, number> = { free: 0, pro: 799, enterprise: 24999 };
for (const p of ["free", "pro", "enterprise"] as const) {
console.log(p.padEnd(10), seats(p), monthlyPrice[p]);
}Output
free 1 0 pro 10 799 enterprise 500 24999
A New Plan Breaks the Build
Example
Adding "team" to the union produces errors in both places that must be updated (TS1360 and TS2741).
type Plan = "free" | "pro" | "team";
function seats(plan: Plan): number {
switch (plan) {
case "free": return 1;
case "pro": return 10;
default:
plan satisfies never;
throw new Error("unreachable");
}
}
const monthlyPrice: Record<Plan, number> = { free: 0, pro: 799 };Compiler output (tsc)
Line 8: error TS1360: Type '"team"' does not satisfy the expected type 'never'.
Line 13: error TS2741: Property 'team' is missing in type '{ free: number; pro: number; }' but required in type 'Record<Plan, number>'.