Hint
Type-level string interpolation — `${"Type"}` creates new string literal types from existing ones
Template literal types extend string literal types using template literal syntax to create new string types programmatically.
// Basic template literal type
type Greeting = `Hello, ${string}!`;
// Matches any string like "Hello, World!" "Hello, Alice!" etc.
type EventName = 'click' | 'focus' | 'blur';
type HandlerName = `on${Capitalize<EventName>}`;
// 'onClick' | 'onFocus' | 'onBlur'
// Distribution — template distributes over union members
type CSSProperty = 'margin' | 'padding';
type CSSDirectional = `${CSSProperty}-${'top' | 'right' | 'bottom' | 'left'}`;
// 'margin-top' | 'margin-right' | ... | 'padding-left' (8 combinations)
Practical patterns:
// Event emitter type-safe API
type EventMap = { 'user:login': User; 'user:logout': void; 'data:update': Data };
type EventEmitter = {
on<K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void): void;
emit<K extends keyof EventMap>(...args: EventMap[K] extends void ? [K] : [K, EventMap[K]]): void;
};
// Building getter/setter pairs
type Accessors<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
} & {
[K in keyof T as `set${Capitalize<string & K>}`]: (val: T[K]) => void;
};
// Route param extraction
type RouteParams<T extends string> =
T extends `${infer _Start}:${infer Param}/${infer Rest}`
? Param | RouteParams<`/${Rest}`>
: T extends `${infer _Start}:${infer Param}`
? Param
: never;
type Params = RouteParams<'/users/:id/posts/:postId'>;
// 'id' | 'postId'