Wednesday, February 8, 2023

TypeScript, using as const for a map where the values are keys to a type

Had a problem with TypeScript at work - I had a map-like object where the values would tell you which field of an existing Type to check.  Here's a (admittedly still kind of confusing) example I made - 

type Qualia = {
  look?: string;
  smell?: string;
  taste?: string;
  sounds?: string;
}
const isIntoxicating =
(impression: Qualia, organ:'nose' | 'mouth'):boolean => {
  const senseForOrgan = {
    'nose': 'smell',
    'mouth': 'taste',
  };
  const whichSense = senseForOrgan[organ];
  return impression[whichSense] === 'intoxicating';
}

console.log(isIntoxicating({'taste':'intoxicating'}, 'mouth'));

So the idea is I want a function (isIntoxicating()) that knows only smells and tastes are intoxicating. So it takes the current sent of Qualia (what I'm experiencing) and lets me find out if an organ is being intoxicated by it. (So in this case I look up "mouth", which the function knows is associated with "taste") Ok bad example but whatever.

This tends to produce TypeScript warnings or errors ala Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Qualia' No index signature with a parameter of type 'string' was found on type 'Qualia'

So the problem is TS isn't smart enough to know that "senseForOrgan" is fixed, and won't be tampered with (remembering that a const array still lets you change the contents of that array, weirdly) The trick is to reassure it by putting "as const" after: 

  const senseForOrgan = {
    'nose': 'smell',
    'mouth': 'taste',
  } as const;

Which tells TS that this isn't going to be changed. 

In a way, that "as const" is a little bit like Object.freeze(), except at TS's compile time rather than runtime. 

Here's more on 'as const'.



No comments:

Post a Comment