Decoding stuff into a TypeScript enum
Staying in a relatively safe realm of TypeScript code, with strict null checks, is all fine and dandy. Sometimes though one has to deal with the cruel outside world: JSON from the server or from the localStorage, user input, URL route parameters, and so on. Then we have to shoehorn the uncontrolled chaos from the outside into our nice algebraic type system which strives for making the impossible states unrepresentable. Here’s a simple example: an enumeration of Languages.
That’s a handy thing to define, because, if you write a function returning some localized text, and happen to forget about English…
The TypeScript transpiler will complain:
a.ts:8:40 - error TS2366: Function lacks ending return statement
and return type does not include 'undefined'.
8 function thanks(language: Language): string {
~~~~~~
That makes sure our pattern matching is exhaustive. Fewer bugs!
We’d certainly want to map the stringly-typed 'de'
, 'fr'
, 'it'
, and whatever else (null
,
undefined
, NaN
, [object Object]
) obtained from the outside, into a member of the Language
union, right at our module’s boundary, before doing anything with the value. We’d need an utility
like this:
Thus, we fall back to German on any kind of nonsense received.
Quite a bit of repetition here, no? It’ll only get worse as the enum grows. We can do better, of
course. Here’s a little trick to avoid the repetition, stay type-safe, and never revise the
toLanguage
function later on, should the enum change.
Smart cast to the rescue! Code on.