Exhaustive Switches in TypeScript
Let's say we all love ice cream. So we define the following enum in TypeScript:
enum IceCreamTaste {
awesome,
meh,
dunnoYet
}
And now we want a switch that returns an answer to your friend's question "How does your ice cream taste?". Like so:
function getAnswer(taste: IceCreamTaste) {
switch (taste) {
case IceCreamTaste.awesome:
return `It's like a party in my mouth!`;
case IceCreamTaste.meh:
return 'Umm I think someone has eaten this before me.';
case IceCreamTaste.dunnoYet:
return 'Lemme try it first, ok?';
default:
throw new Error('unreachable case');
}
}
Now, it sure would be nice if TypeScript could hand-hold us a little and verify that we've covered all the possible values of the IceCreamTaste
enum, right? Otherwise if I add, say, a new chocolate
taste (which is obviously above awesome
), I would have to remember to update all the switches that reference the enum and...oh hey, welcome, bugs!
Unreachability
Fortunately TypeScript 2+ sports a useful type called never
(sometime referred to as the "bottom" type) which represents a value that can never occur.
OK, so how is that useful, you ask? Well, because our IceCreamTaste
switch is exactly such a case - we want to tell the TypeScript compiler that the default
branch of the switch statement is one of those "code execution can never get there" blocks.
Let's define an UnreachableCaseError
:
class UnreachableCaseError extends Error {
constructor(val: never) {
super(`Unreachable case: ${val}`);
}
}
Now if we plug this error into our ice cream switch, we get this type safe bliss:
function getAnswer(taste: IceCreamTaste) {
switch (taste) {
case IceCreamTaste.awesome:
return `It's like a party in my mouth!`;
case IceCreamTaste.meh:
return 'Umm I think someone has eaten this before me.';
case IceCreamTaste.dunnoYet:
return 'Lemme try it first, ok?';
default:
throw new UnreachableCaseError(taste);
}
}
Try commenting out one of the cases and TypeScript will report an Argument of type 'IceCreamTaste.dunnoYet' is not assignable to parameter of type 'never'
error.
Happy exhaustive switching! I'm @tomasbrambora