Template literal types
A template literal type builds a string-literal or string-pattern type by interpolating other types into a backtick template, distributing over unions to produce every combination. Its main use is remapping the keys of a mapped type. The four case operators check their arguments today; the template pattern itself parses but does not yet fully resolve.
A template literal type builds a string-literal or string-pattern type by interpolating other types into a backtick template. It is the type-level counterpart of a runtime template string: the same ` ...${...}... ` shape, but in type position, producing a type rather than a value.
type Getter = `get${Capitalize<Field>}`
type Route = `/api/${Version}/${Resource}`
If you have used TypeScript, these are the same template literal types. They let you describe strings that follow a pattern, such as an event name prefixed with on, a route assembled from segments, or an accessor name derived from a field.
Before going further, one honest caveat about the current state. The template-literal type form now parses: a ` ...${...}... ` in type position is accepted syntax. What is not yet guaranteed is that its interpolated holes are fully resolved — the parser is lenient about a template's interior, so acceptance is weak evidence the pattern computes the string type described below. The four case operators, by contrast, are recognized intrinsics whose type arguments are checked (a bogus argument is rejected). Read the rest of this page as the intended behavior, with the resolution of template-literal patterns still being brought up.
How interpolation works
A template literal type joins fixed text and interpolated types into a single string type. When a hole is filled by a single literal, the result is one string literal. When a hole is filled by a union, the type distributes across the members and produces the cross-product of every combination.
type Version = "v1" | "v2"
type Resource = "users" | "posts"
type Route = `/api/${Version}/${Resource}`
// "/api/v1/users" | "/api/v1/posts" | "/api/v2/users" | "/api/v2/posts"
Each union hole multiplies the set of results, so two two-member unions yield four string literals, as above.
Key remapping in mapped types
The primary use of template literal types is remapping the keys of a mapped type. You interpolate each existing key into a template to compute a new key name:
type Getters<T> = { [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K] }
This turns a field named name into a method named getName, and so on for each key. The K & string narrows the key to a string before splicing it, since only string keys can be interpolated into a template.
Case operators
Four intrinsic case operators transform the string inside a template:
Uppercase<S>makes every character upper case.Lowercase<S>makes every character lower case.Capitalize<S>upper-cases the first character.Uncapitalize<S>lower-cases the first character.
type A = Uppercase<"ready"> // "READY"
type B = Capitalize<"name"> // "Name"
These operate only on string-literal types, at compile time, and are how a template like ` get${Capitalize<K>} produces getName from name. Unlike the template pattern itself, these four are recognized intrinsics: their type arguments are checked, so Uppercase<"ready"> is accepted while Uppercase<Zorp> is rejected for the unresolved Zorp`.
Why it works this way
Template literal types let the type system compute precise string types instead of falling back to a plain string. That precision is what makes key remapping useful: the derived key names are exact literals the rest of the type system can check against, so a generated getName method is known by that name rather than by "some string." Distributing over unions keeps the computation honest when a hole has several possible values, producing every real combination rather than a vague pattern. A hole can never carry a non-string type without narrowing it first, which keeps a malformed source from silently producing a meaningless string.
Limitations
- Template-literal type syntax parses but does not yet fully resolve. A `
...${...}...` in type position is accepted, but the interior is checked leniently, so acceptance does not prove the pattern computes the string type shown here. Rely on it as recognized syntax, not a proven transformation, for now. - The case operators exist and check their arguments.
Uppercase,Lowercase,Capitalize, andUncapitalizeare registered intrinsics whose type arguments are validated; an unresolved argument is rejected. Whether every case fully computes its result string is not yet established. - Type position only. Template literal types live in type position and are distinct from runtime template strings, even though they share the backtick syntax.