Skip to content
Skip to content
DocsTypeScript ExpertexpertTemplate Literal Types
Chapter 3 of 10·expert·9 min read

Template Literal Types

Kiểu Template Literal

Build string types dynamically with template literal syntax

Hover or tap any paragraph to see Vietnamese translation

Basic Syntax

Template literal types build new string types by embedding other types inside a backtick string — identical syntax to JavaScript template literals but operating entirely at the type level.

basic-template-literal.ts
1type Greeting = `Hello, ${string}`;23const a: Greeting = "Hello, world";   // OK4const b: Greeting = "Hello, Alice";   // OK5const c: Greeting = "Hi there";       // Error — does not match pattern67// Embedding a literal type8type EventName = "click" | "focus" | "blur";9type HandlerName = `on${Capitalize<EventName>}`;10// = "onClick" | "onFocus" | "onBlur"1112// Embedding number produces string representation13type GridRow = `row-${number}`;14const r: GridRow = "row-42"; // OK

Embedding a non-literal type like string or number produces a pattern-matching constraint rather than a finite union. TypeScript enforces that any value assigned must match the pattern at compile time.

Combining with Unions

When a template literal type contains a union, TypeScript automatically computes the Cartesian product — every combination of the member strings.

union-combinations.ts
1type Direction = "top" | "right" | "bottom" | "left";2type Size = "sm" | "md" | "lg";34// All combinations: "top-sm" | "top-md" | "top-lg" | "right-sm" | ...5type Spacing = `${Direction}-${Size}`;67// Useful for CSS utility class names8type Margin = `m${Direction extends string ? Capitalize<Direction> : never}`;9// = "mTop" | "mRight" | "mBottom" | "mLeft"1011// Multiple unions multiply out12type Color = "red" | "blue";13type Shade = "light" | "dark";14type ColorShade = `${Shade}-${Color}`;15// = "light-red" | "light-blue" | "dark-red" | "dark-blue"
Info
The Cartesian product grows exponentially. TypeScript enforces a union member limit near 100,000 — combine large unions with care.

Intrinsic String Manipulation Types

TypeScript ships four compiler-intrinsic string manipulation types. They are implemented at the compiler level and cannot be replicated in user-land TypeScript.

intrinsic-string-types.ts
1// Uppercase<S> — converts every character to upper case2type U = Uppercase<"hello">;        // "HELLO"3type Env = Uppercase<"dev" | "prod">; // "DEV" | "PROD"45// Lowercase<S> — converts every character to lower case6type L = Lowercase<"WORLD">;        // "world"78// Capitalize<S> — upper-cases the first character only9type C = Capitalize<"firstName">;   // "FirstName"1011// Uncapitalize<S> — lower-cases the first character only12type UC = Uncapitalize<"FirstName">; // "firstName"

Extracting Parts with infer

Combining template literal types with conditional types and infer lets you parse string types — extracting named portions of a string pattern at the type level.

infer-template.ts
1// Extract everything after a prefix2type StripPrefix<S extends string, Prefix extends string> =3	S extends `${Prefix}${infer Rest}` ? Rest : S;45type Stripped = StripPrefix<"on:click", "on:">; // "click"6type Same = StripPrefix<"click", "on:">;         // "click" (no match, returns S)78// Extract head and tail of a dot-separated path9type Head<S extends string> =10	S extends `${infer H}.${string}` ? H : S;1112type Tail<S extends string> =

Practical: Event Name Types

One of the most practical uses of template literal types is building type-safe event emitter APIs where event names are automatically derived from model names.

event-types.ts
1type EntityEvent<Entity extends string, Action extends string> =2	`${Lowercase<Entity>}:${Action}`;34type UserEvent = EntityEvent<"User", "created" | "updated" | "deleted">;5// = "user:created" | "user:updated" | "user:deleted"67type OrderEvent = EntityEvent<"Order", "placed" | "shipped" | "cancelled">;89type AppEvent = UserEvent | OrderEvent;1011// Type-safe event emitter12declare function on<E extends AppEvent>(

Practical: CSS Property Types

Template literal types excel at modelling CSS string patterns — shorthand properties, custom properties, breakpoint variants, and more.

css-types.ts
1// CSS custom property names must start with --2type CSSVar = `--${string}`;34declare function setVar(name: CSSVar, value: string): void;5setVar("--primary-color", "#3b82f6"); // OK6setVar("primary-color", "#3b82f6");   // Error — missing --78// Spacing scale: t-shirt sizes mapped to sides9type Side = "top" | "right" | "bottom" | "left" | "x" | "y";10type SpacingScale = 0 | 1 | 2 | 4 | 8 | 16;11type SpacingClass = `p${Side}-${SpacingScale}` | `m${Side}-${SpacingScale}`;12

Deep Object Path Types

Template literals combined with mapped and conditional types let you model deep nested object paths — a pattern used by React Hook Form, Zod, and similar libraries.

deep-paths.ts
1type Paths<T, Prefix extends string = ""> =2	T extends object3		? {4				[K in keyof T]: K extends string5					? Prefix extends ""6						? K | Paths<T[K], K>7						: `${Prefix}.${K}` | Paths<T[K], `${Prefix}.${K}`>8					: never;9			}[keyof T]10		: never;1112type Config = {

Summary

  • Template literal types use backtick syntax to build new string types from existing ones
  • Combining with unions produces the Cartesian product of all possible string combinations
  • Uppercase, Lowercase, Capitalize, Uncapitalize are compiler-intrinsic utilities
  • infer inside a template literal extracts named portions of a string type
  • Practical applications: type-safe event names, CSS utility classes, deep object path types
  • Template literal types exist only at compile time — they have zero runtime overhead
  • Built: 6/25/2026, 3:03:36 PM