Difficulty: Advanced
Template literal types in TypeScript bring template string syntax into the type system. Just as JavaScript template literals let you embed expressions in strings at runtime, template literal types let you embed type expressions in string literal types at compile time. The syntax uses backticks with `${}` placeholders containing other types, producing new string literal types.
When template literal types contain union types in their placeholders, TypeScript distributes over all possible combinations. For example, `` `on${'Click' | 'Hover'}` `` produces `'onClick' | 'onHover'`. With multiple placeholders, you get the cartesian product: `` `${'get' | 'set'}${'Name' | 'Age'}` `` produces `'getName' | 'getAge' | 'setName' | 'setAge'`. This combinatorial power lets you express complex string patterns concisely.
TypeScript provides four intrinsic string manipulation types: `Uppercase<T>`, `Lowercase<T>`, `Capitalize<T>`, and `Uncapitalize<T>`. These transform string literal types at the type level. Combined with template literal types and mapped types, they enable patterns like generating getter/setter names, event handler names, or CSS property types from a set of base names.
Template literal types are commonly used in library typings to provide precise string-based APIs. For instance, you can type a CSS-in-JS library to only accept valid CSS property names, or type a routing library to extract parameter names from URL patterns like `/users/:id/posts/:postId`. These types make previously stringly-typed APIs fully type-safe.
type Color = "red" | "blue" | "green";
type Size = "sm" | "md" | "lg";
type ClassName = `${Size}-${Color}`;
// "sm-red" | "sm-blue" | "sm-green" | "md-red" | ...
function applyClass(cls: ClassName): string {
return `Applied class: ${cls}`;
}
console.log(applyClass("sm-red"));
console.log(applyClass("lg-blue"));
TypeScript computes the cartesian product of Size and Color unions, producing all 9 valid combinations as the ClassName type.
type EventName = "click" | "focus" | "blur";
type Handler = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"
type UpperEvents = Uppercase<EventName>;
// "CLICK" | "FOCUS" | "BLUR"
const handlers: Record<Handler, () => void> = {
onClick: () => console.log("clicked"),
onFocus: () => console.log("focused"),
onBlur: () => console.log("blurred")
};
handlers.onClick();
handlers.onFocus();
Capitalize transforms each member of the EventName union, and the template literal prepends 'on'. The result is a precise type for event handler names.
type ExtractParam<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParam<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: never;
type Route = "/users/:userId/posts/:postId";
type Params = ExtractParam<Route>; // "userId" | "postId"
function buildUrl(route: string, params: Record<string, string>): string {
return route.replace(/:([a-zA-Z]+)/g, (_, key) => params[key] || `:${key}`);
}
console.log(buildUrl("/users/:userId/posts/:postId", { userId: "42", postId: "7" }));
The ExtractParam type recursively extracts parameter names from a URL pattern. The runtime buildUrl function replaces :param placeholders with actual values.
template literal types, string manipulation types, Uppercase, Lowercase, Capitalize, Uncapitalize