Want a faster signal before interviews? Take the Interview Ready Check →

DAY 30 OF 30

Template literal types

Yesterday, in one sentence

What are conditional types in TypeScript, and what does infer do?

Interview question

What is a mapped type in TypeScript?

A template literal type builds a string literal typeLiteral Type out of other types, using the same backtick syntax as a JavaScript template literal.

type Alignment = "hero" | "villain";

type Label = `marvel-${Alignment}`;
// "marvel-hero" | "marvel-villain"

Interpolating a unionUnion Type produces every combination, so the result is a union too. With two unions you get all the pairs.

type Power = "flight" | "healing";
type Level = "basic" | "max";

type Upgrade = `${Power}-${Level}`;
// "flight-basic" | "flight-max" | "healing-basic" | "healing-max"

The real use is renaming keys in a mapped typeMapped Type. Say we have a Hero type and we want an event handler type derived from it.

On the left, type Hero has attack and heal, both of type string. An arrow points right to type HeroEvents, which has onAttack and onHeal, both functions returning string.

Three things get us there.

First, loop over the keys with [K in keyof T].

Second, give each generated key a new name with an as clause, which is where the template literal goes.

[K in keyof T as `on${K}`]

That does not compile. keyof T can be a string, a number or a symbol, and only strings are allowed in a template literal, so we have to make sure it is a string. An intersection typeIntersection Type does that.

[K in keyof T as `on${string & K}`]

Third, attack needs to be onAttack. We have already added the on, but the key itself still needs capitalising. Capitalize is a built in type that does exactly that, and TypeScript has three others like it: Uppercase, Lowercase and Uncapitalize.

Put together we get this.

// map over each key of T, renaming it to on<Key>
type EventHandlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}`]: () => string;
};

type Hero = {
  attack: string;
  heal: string;
};

type HeroEvents = EventHandlers<Hero>;
// { onAttack: () => string; onHeal: () => string }

Check yourself

What is Route here?
type Section = "heroes" | "villains"

type Route = `/${Section}/list`
What are template literal types in TypeScript?