← thecodex.expert · The Codex Family of Knowledge
TypeScript

Template Literal Types

Introduced in TypeScript 4.1, template literal types let you build a type the same way you’d build a string — by interpolating other types directly into a literal pattern.

TypeScript 6.0 Since TypeScript 4.1 Last verified:
Where this bites you

`${number}px` reads as "a CSS pixel length", but ${number} means "anything JavaScript can parse as a number", which is broader than what CSS accepts. TypeScript happily accepts "0x10px" (hex) and "5.px". A browser rejects both, and assigning a rejected value to style.width is silently ignored, so the element keeps its old width with no error anywhere.

TypeScriptpx_bug.ts
type Px = `${number}px`;

function setWidth(el: { style: { width: string } }, width: Px) {
    el.style.width = width;
}

const fromConfig: Px = "0x10px";   // accepted: Number("0x10") is 16
setWidth(div, fromConfig);
// Chromium: div.style.width stays "100px" -- "0x10px" (and "5.px") are not valid CSS
Canonical Definition

A template literal type looks exactly like a JavaScript template string but operates on types: interpolating a union type inside the backticks produces a new union covering every combination. Introduced in TypeScript 4.1 (2020) alongside the key-remapping as clause in mapped types (covered earlier), and the two are often used together — but they're separate features, not one built on the other. The as clause accepts any computed key expression, of which a template literal type is only the most common; key remapping works just as well with a plain mapped type or a conditional type as the new key. On their own, template literal types let a type precisely describe string patterns like event names, CSS properties, or API route paths.

Interpolating a union produces every combination

The result is a new union covering every combination of the interpolated union's members — not a single string, but every valid EventName the pattern can produce.

TypeScriptbasic_template.ts
type EventName = "click" | "scroll" | "resize";
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onScroll" | "onResize"

function addHandler(name: HandlerName) { /* ... */ }
addHandler("onClick");    // fine
// addHandler("onclick");  // COMPILE ERROR: lowercase doesn't match the pattern

A template literal type checks the shape of a string, using TypeScript's own idea of a number string. Checked with tsc 6.0.3: "1e3px", "0x10px", "5.px", ".5px" and " 5px" were all accepted, while "NaNpx", "Infinitypx" and "1_000px" were rejected. When the value has to be valid for something stricter, build it from a real number instead of accepting arbitrary strings:

TypeScriptpx_fix.ts
type Px = `${number}px`;

function px(n: number): Px {
    if (!Number.isFinite(n)) throw new Error(`not a finite number: ${n}`);
    return `${n}px`;
}

const fromConfig: Px = px(16);   // "16px" -- always built from a real number
console.log(fromConfig);

Combining with mapped types: real, practical use

This is exactly the pattern used earlier for the Getters<T> example — the two features (mapped types and template literal types) compose directly.

TypeScriptcss_properties.ts
type CSSProperty = "color" | "background" | "border";
type CSSVariable = `--${CSSProperty}`;
// "--color" | "--background" | "--border"

const styles: Record<CSSVariable, string> = {
    "--color": "blue",
    "--background": "white",
    "--border": "1px solid black",
};

Built-in intrinsic string manipulation types

TypeScript ships four built-in type-level string transformations — Uppercase, Lowercase, Capitalize, Uncapitalize — that operate purely on string literal types at compile time, with no runtime equivalent needed since they're erased entirely.

TypeScriptintrinsic_types.ts
type Loud = Uppercase<"hello">;        // "HELLO"
type Quiet = Lowercase<"HELLO">;        // "hello"
type Title = Capitalize<"hello">;       // "Hello"
type Mini = Uncapitalize<"Hello">;      // "hello"

Sources

1
Microsoft. TypeScript Handbook, "Template Literal Types", typescriptlang.org/docs/handbook/2/template-literal-types.html.