44

Exhaustive Switches in TypeScript

 6 years ago
source link: https://www.tuicool.com/articles/hit/yMrUj2e
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK