Topics

Jump to a topic.

Search questions

Find a question by title, tag, or summary.

What are template literal types, and what are they used for?

Questions in this chapter (6)
  • Draft
  • medium
  • Askedoften
  • Verified October 2026

Answer

A template literal type is the backtick syntax used in a type: `${A}-${B}`. It does three jobs.

  1. Build string unions. Placeholders that are literal types are concatenated. Union placeholders are cross-multiplied, so `${"en" | "ja"}_${"title" | "body"}` is four literals. This is how you derive event names (`${K}Changed`) or CSS-like tokens from existing unions.
  2. Describe a string pattern. A placeholder of string, number or bigint makes a non-literal pattern (boolean just expands to "true" | "false", checked): `${number}px` accepts "12px" and rejects "12em"; `--${string}` accepts any string with that prefix. Patterns also work as mapped-type key sources, e.g. `data-${string}`.
  3. Parse strings with infer. In a conditional type, S extends `${infer A}-${infer B}` splits the string. A takes the shortest match up to the first delimiter and the last placeholder takes the rest. With adjacent placeholders each non-final infer captures a single character. Add infer N extends number (TS 4.8) to turn "42" into the literal 42.

Intrinsic string types. Uppercase, Lowercase, Capitalize, Uncapitalize are implemented inside the compiler (intrinsic in lib.es5.d.ts; PR 40580), not in TypeScript. They distribute over unions and are the usual way to turn name into getName.

Constraints. Placeholders must be string | number | bigint | boolean | null | undefined; use string & K for a key type that may include symbols. A union in each position multiplies, and the compiler errors (TS2590) when the result becomes too large, so generate big sets ahead of time instead.

Typical uses: typed event or route names, parsing route params (/users/:id), typed on("xChanged") APIs, CSS unit strings, and key renaming in mapped types.

Example

// TypeScript 7.0.2 and 5.9.3, --strict
type Key = `${"en" | "ja"}_${"title" | "body"}`; // "en_title" | "en_body" | "ja_title" | "ja_body"
type Getter = `get${Capitalize<"name" | "age">}`; // "getName" | "getAge"

type Px = `${number}px`;
const ok: Px = "12px";
const bad: Px = "12em";
// Error TS2322: Type '"12em"' is not assignable to type '`${number}px`'.

type Pair = "a-b-c" extends `${infer A}-${infer B}` ? [A, B] : never; // ["a", "b-c"]
type Chars = "abc" extends `${infer A}${infer B}` ? [A, B] : never; // ["a", "bc"]
type Num = "42" extends `${infer N extends number}` ? N : never; // 42

type Split<S extends string, D extends string> =
  S extends `${infer Head}${D}${infer Tail}` ? [Head, ...Split<Tail, D>] : [S];
type Parts = Split<"a.b.c", ".">; // ["a", "b", "c"]

type Params<R> = R extends `${string}:${infer P}/${infer Rest}`
  ? P | Params<`/${Rest}`>
  : R extends `${string}:${infer P}` ? P : never;
type RouteParams = Params<"/users/:id/posts/:postId">; // "id" | "postId"

type Prefixed = `--${string}`;
const flag: Prefixed = "-flag";
// Error TS2322: Type '"-flag"' is not assignable to type '`--${string}`'.

Pitfalls

  • Interpolating a type that may be symbol. `x${symbol}` is TS2322; narrow with string & K.
  • Generating large cross products. Five ten-member unions (100,000 members) gave TS2590 on both compilers, while four (10,000) compiled.
  • Expecting infer to backtrack. The first match wins: "a-b-c" splits at the first -, giving "b-c" as the tail.
  • Assuming a pattern type is checked at runtime. `${number}px` only checks literals the compiler can see; a string variable is not assignable to it without a cast or guard.

Follow-ups

What does Uppercase<string> give?
On 5.9.3 it prints as Uppercase<string>, and a plain string is not assignable to it (TS2322). The PR 40580 description listed string, so trust the checked behaviour, not the PR text.
How do you make an on method that types its callback from the event name?
Take a generic Key extends string & keyof T, accept `${Key}Changed` and type the callback as (v: T[Key]) => void; inference recovers Key from the pattern (handbook example).

Exercises

Kebab to camel

Write CamelCase<S> so CamelCase<"foo-bar-baz"> is "fooBarBaz".

type CamelCase<S extends string> = unknown;