Template Literal Types
Build string types with `on${Event}`, extract route params, and type CSS-like APIs using TypeScript template literal types.
- typescript
- template-literal
Template literal types mirror JS template strings in the type system:
type World = 'world';
type Greeting = `hello ${World}`; // 'hello world'
They compose unions:
type Corner = `${'top' | 'bottom'}-${'left' | 'right'}`;
// 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
Docs: Template Literal Types.
Event handler prop names
type Ev = 'click' | 'focus' | 'blur';
type HandlerName = `on${Capitalize<Ev>}`;
// 'onClick' | 'onFocus' | 'onBlur'
Intrinsic string modifiers: Uppercase, Lowercase, Capitalize, Uncapitalize.
Extracting pieces with infer
type ParseEvent<T> = T extends `on${infer E}` ? Uncapitalize<E> : never;
type E = ParseEvent<'onClick'>; // 'click'
type RouteParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | RouteParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: never;
type P = RouteParams<'/users/:id/posts/:postId'>; // 'id' | 'postId'
(Real routers use battle-tested types; this shows the pattern.)
CSS unit patterns
type CssLength = `${number}px` | `${number}rem` | `${number}%` | 0;
function margin(value: CssLength) {
return { margin: value };
}
margin('8px');
// margin('8em'); // error if not in union
API path builders
type ApiVersion = 'v1' | 'v2';
type Resource = 'users' | 'orders';
type Endpoint = `/api/${ApiVersion}/${Resource}`;
function get(path: Endpoint) {
return fetch(path);
}
get('/api/v1/users');
Mapped keys with templates
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
See mapped types intro.
Pattern matching unions
type Email = `${string}@${string}.${string}`; // weak but documents intent
Don’t over-trust string structure types — still validate emails at runtime.
When they shine in frontend
- Design-token names:
color.primary.500style paths. - i18n keys if you generate a key union.
- Query param builders.
- Library typings for CSS-in-JS.
Complexity costs
Template literal types can explode unions (A × B × C). IDE performance suffers. Keep unions small; split intermediate aliases; avoid open string interpolations when a finite union works.
// Can get huge
type Huge = `${'a' | 'b' | 'c'}-${'1' | '2' | '3'}-${'x' | 'y' | 'z'}`;
Footguns
${string}absorbs almost everything — constraints go soft.- Number interpolation —
`${number}`is a pattern, not math. - Runtime — types don’t format strings; write real functions.
- Recursion depth — deep route parsers need care.
Interview out-loud answer
“Template literal types compose string unions and can infer chunks with infer. I use them for event prop names, path patterns, and mapped getter keys. I keep unions finite for performance and still validate user strings at runtime.”
Uppercase helpers in real APIs
type Http = 'get' | 'post';
type Method = Uppercase<Http>; // 'GET' | 'POST'
Intrinsic string modifiers are compile-time only. Runtime still needs .toUpperCase() if you construct strings dynamically — types won’t convert values.
Extra practice
Write a minimal demo in a scratch file or the playground: one happy path, one failure path, and one boundary input. If you cannot exhibit a bug that the pattern prevents, you do not own the concept yet — re-read the primary docs linked below and tighten the example until the failure is obvious.
Related on this site
- Literal types
- Mapped types intro
- Conditional types intro
- infer keyword basics
- keyof typeof and indexed access
Further reading
Related guides
- Basic Types and AnnotationsPrimitives, arrays, objects, and function annotations TypeScript actually checks — plus where inference is enough and where it is not.
- Branded Types for IDsNominal-style UserId vs OrderId in TypeScript — prevent ID mixups at compile time with brands, parsers, and form boundaries.
- Conditional Types IntroT extends U ? X : Y — how TypeScript picks types from conditions, distributes over unions, and powers utility types you already use.
- Declaration Files and DefinitelyTypedHow .d.ts files describe JS to TypeScript, when to use @types packages, module augmentation, and writing minimal ambient types for untyped libs.
- Discriminated Unions for UI StateModel loading, success, and error as mutually exclusive variants so TypeScript and your UI cannot show impossible states.