For me there is a distinction to be made when it comes to defensive programming. On the type level you should be as defensive as possible. Your types should result in compiler errors if you do not handle new cases in the future. Make your types as small as possible, just like you did in your article (use a sum of three distinct values instead of simply a string).
On value level on the other hand, I really dislike defensive programming, usually because it means that your compiler's type system is not strong enough to check these at compiler time. The prime example here is the classic null. TypeScript has non-nullable types, but for example Java programmers have to put if(x != null) everywhere, just in case.
The value level is short and concise, it only contains what I want to do (ie mapping a signal to some action) with not much boilerplate syntax. The type level code is almost the same length, guaranteeing that adding future cases will result in a compiler error.
For me there is a distinction to be made when it comes to defensive programming. On the type level you should be as defensive as possible. Your types should result in compiler errors if you do not handle new cases in the future. Make your types as small as possible, just like you did in your article (use a sum of three distinct values instead of simply a string).
On value level on the other hand, I really dislike defensive programming, usually because it means that your compiler's type system is not strong enough to check these at compiler time. The prime example here is the classic
null. TypeScript has non-nullable types, but for example Java programmers have to putif(x != null)everywhere, just in case.I would write this code snippet like this:
The value level is short and concise, it only contains what I want to do (ie mapping a signal to some action) with not much boilerplate syntax. The type level code is almost the same length, guaranteeing that adding future cases will result in a compiler error.
OP: This is another great solution ^