Less Redux Boilerplate Using Typescript Template Literals

Mikhail Boutylin
The Startup

--

Typescript 4.1 introduced an incredibly powerful feature called Template Literal Types. It allows developers to write better typing for strings, so typescript can check them to match certain template:

In the example above a variable of type Percent can accept a string value, that starts with a numeric string and terminates with % .

Another, even (in my opinion) more important use case is possibility to map keys of an object.

Let get a step back to explain what happens in detail. Suppose Getters would have the following definition:

K in keyof T means that interface transformed with Getters will have same keys as initial one. () => T[K] means that values corresponding to those keys will be functions that return T[K]. And T[K] is the original type of value corresponding to the key K. This will return an interface like:

However it’s not a conventional naming for get functions. Prior to TS 4.1 there was no way to solve this issue (other than explicitly mapping each key in the code). But now with addition of Template Literal Types we can transform keys in a reusable way. To transform a key you need to use as operator which on the left has selection of keys and transformation of keys on the right.

Now let analyze `get${Capitalize<string & K>}`. A key (e.g. name) gets capitalized (will become Name) and prepended by get. And since a key of object can benumber | string | symbol, we need to enforce that keys of object passed to Getters has string keys string & K. If command `get${Capitalize<string & K>}` is still unclear, take a look at the diagram bellow.

Ok, but how it’s related to redux ? :)

To write performant application with React we need a way to re-render components only when this is needed. For this purpose we should only select from store only information needed to display actual state. Let suppose we have following state shape for active user in a video game:

If your component selects full activePlayer object, it will cause re-renderings even in case this is not needed (e.g. gold field is updated, but it’s not rendered in the component).

One way to avoid those re-renderings, is to create a selector for each field we need to render individually:

Looks terrible?

A logical question here would be, if we can write an utility that could create those selectors for us by just accepting selectActiveUser (used as a base selector), and the default activeUser state (so the utility will know what keys does this object have); Let call it selectorify.

And in reality it turns out a pretty easy thing to do. We just need to map keys and values of default state. For example key name should become selectName and the corresponding value, should be a selector which will return property name of active user. For simplicity, let take a look to version without ts:

And this is version of ts declaration (from a .d.ts) file:

Now you can create 100% typed selectors for each key of your feature.

P.S.

--

--