Say my server responds with the following: 'a', 'b', or 'c'. These map to 'alpha', 'beta' or 'gamma' in my app. If I were using JS, I'd do the following to translate:
const mapping = {
a: 'alpha',
b: 'beta',
c: 'gamma'
}
const lookUp = serverResponse => mapping[serverResponse];
I can do the equivalent in TypeScript. However, let's say I also want the guarantee that in case the server adds 'd' for 'delta', I can modify one location in my code and let TS help me with its type system to guide the rest of my refactoring.
type ServerResponse = 'a' | 'b' | 'c';
type ActualMeaning = 'alpha' | 'beta' | 'gamma';
const mapping: { [key in ServerResponse]: ActualMeaning } = {
a: 'alpha',
b: 'beta',
c: 'gamma'
} as const;
const shouldNeverReachHere = (x: never): never => {
throw new Error(`This value not exhaustively handled: ${x}`);
}
const lookup = (serverResponse: ServerResponse): ActualMeaning => {
if (serverResponse in mapping) return mapping[serverResponse];
return shouldNeverReachHere(serverResponse);
}
I'd expect that since the mapping is known at compile time (because of the as const), the exhaustiveness check for shouldNeverReachHere(serverResponse) would have worked. However, I get the following error for that line:
Argument of type '"a" | "b" | "c" is not assignable to parameter of type 'never'.
Type '"a"' is not assignable to type 'never'. ts(2345)
If I change the function to use a switch instead of looking up in an object, it works. But switch statements are clunky. I also want to convert both ways, which makes objects very easy to work with, as opposed to a switch.
Any idea how I could make the above code work in TypeScript?