1. Introduction
Template literal types, introduced in TypeScript 4.1, let you construct new string literal types using the same backtick syntax as JavaScript template literals, but with type placeholders instead of runtime expressions. Interpolating a union type inside a template literal type produces the cross-product union of all possible resulting strings, which makes them powerful for modeling patterns like CSS property names, event handler names, and structured string identifiers with full compile-time type safety.
Cricket analogy: Just as a fixture generator can cross the union of eight IPL teams with the union of home/away venues to compile every possible match-slot string ahead of the season, a template literal type crosses unions inside backticks to produce every valid string at compile time.
2. Syntax
type Direction = "top" | "right" | "bottom" | "left";
type Margin = `margin-${Direction}`;
// "margin-top" | "margin-right" | "margin-bottom" | "margin-left"
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
// intrinsic string manipulation types used with template literals
type Upper = Uppercase<"abc">; // "ABC"
type Lower = Lowercase<"ABC">; // "abc"
type Cap = Capitalize<"abc">; // "Abc"
type Uncap = Uncapitalize<"Abc">; // "abc"
type Coordinate = `${number},${number}`;3. Explanation
A template literal type is written with backticks and ${...} placeholders, exactly like a JS template literal, but each placeholder holds a type rather than a runtime expression — typically a string, number, boolean, bigint literal type, or a union of these. When a union is interpolated, TypeScript distributes across it (much like a distributive conditional type) and produces the union of every combination, so ` margin-${Direction} with a four-member union Direction` yields a four-member union of concrete margin strings.
Cricket analogy: Just as a scorecard template over-${BallNumber} with BallNumber as the union 1|2|3|4|5|6 distributes to produce six distinct over-position strings, margin-${Direction} with a four-member Direction union distributes to produce four concrete margin strings.
TypeScript ships four intrinsic string manipulation types — Uppercase<S>, Lowercase<S>, Capitalize<S>, Uncapitalize<S> — specifically to be composed with template literal types, since ordinary string methods only exist at runtime and have no type-level equivalent otherwise. Numeric placeholders (` ${number},${number} `) don't produce a union of every possible number; instead they widen to match any string that has the right lexical shape (digits in each position), which TypeScript checks structurally against literal string values.
Cricket analogy: A scorecard needs Uppercase<S> to render MSD from a batter's initials since string methods only run at runtime, while a numeric placeholder like over-${number}.${number} accepts any over-and-ball string of that shape, such as 12.4, without enumerating every possible over.
Template literal types are commonly combined with mapped types and key remapping (as) to derive new object shapes, e.g. { [K in keyof T as on${Capitalize<string & K>}]: (value: T[K]) => void } to auto-generate a matching set of event handler prop names from a props interface.
Gotcha: interpolating a large union inside a template literal type creates the full cross-product of possible strings, which can grow combinatorially — interpolating two independent 10-member unions produces up to 100 literal string types. TypeScript imposes a limit on the number of union members it will eagerly expand (historically capped around 100,000 combinations before erroring or falling back to a wider string type), so combining several large unions in one template can hit compiler limits or seriously slow down type-checking.
4. Example
type Direction = "top" | "right" | "bottom" | "left";
type Margin = `margin-${Direction}`;
const margins: Record<Margin, number> = {
"margin-top": 4,
"margin-right": 8,
"margin-bottom": 4,
"margin-left": 8,
};
console.log(Object.keys(margins).join(", "));
type EventName<T extends string> = `on${Capitalize<T>}`;
function on<T extends string>(event: EventName<T>, handler: () => void) {
console.log(`registered ${event}`);
handler();
}
on("onClick", () => console.log("clicked!"));
on("onHover", () => console.log("hovered!"));
type Coordinate = `${number},${number}`;
const point: Coordinate = "10,20";
console.log(point);5. Output
margin-top, margin-right, margin-bottom, margin-left
registered onClick
clicked!
registered onHover
hovered!
10,206. Key Takeaways
- Template literal types build new string literal types with backtick syntax and
${Type}placeholders. - Interpolating a union type distributes to produce the cross-product union of resulting strings.
Uppercase,Lowercase,Capitalize, andUncapitalizeare intrinsic types made for composing with template literal types.- Numeric/string placeholders like
${number}match structurally rather than enumerating every possible number. - Combining multiple large unions inside one template literal type can hit combinatorial compiler limits.
Practice what you learned
1. What does `type Margin = \`margin-${Direction}\`` produce, given `type Direction = "top" | "right" | "bottom" | "left"`?
2. Which intrinsic type would you use to transform the literal type "click" into "Click" at the type level?
3. Gotcha: what is the risk of interpolating multiple large union types together inside one template literal type?
4. In `type Coordinate = \`${number},${number}\``, does this type enumerate every possible number combination as separate literal types?
5. What is a common combined use of template literal types and mapped types?
Was this page helpful?
You May Also Like
Mapped Types in TypeScript
Transform every property of an existing type into a new type using `{ [K in keyof T]: ... }`, with modifiers and key remapping.
Literal Types in TypeScript
Narrow a type down to one exact value -- a specific string, number, or boolean -- and combine literals into precise unions.
keyof and typeof Operators in TypeScript
Use `keyof T` to get a union of a type's property names, and `typeof x` in a type position to extract the type of a value.
Utility Types in TypeScript
Learn TypeScript's built-in generic utility types — Partial, Required, Readonly, Pick, Omit, and Record — for transforming existing types.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics