Template Literal Types¶
Template literal types look like JavaScript template strings, but they operate at the type level:
type Greeting = `Hello, ${string}`
This means:
“Any string that starts with
Hello,followed by anything.”
Example:
let g: Greeting
g = "Hello, world" // ✔ OK
g = "Hello, TypeScript" // ✔ OK
g = "Hi there" // ❌ Error
Template literal types let you build pattern‑based string types.
Building String‑Based Types¶
Let’s start with simple combinations.
Combining unions¶
type Size = "small" | "medium" | "large"
type Color = "red" | "blue"
type Variant = `${Size}-${Color}`
Result:
// "small-red" | "small-blue" |
// "medium-red" | "medium-blue" |
// "large-red" | "large-blue"
This is incredibly useful for:
- CSS utility classes
- design systems
- component variants
Prefixing and suffixing¶
type EventName<T extends string> = `on${Capitalize<T>}`
Usage:
type E = EventName<"click" | "hover">
// "onClick" | "onHover"
This is how many UI frameworks type event handlers.
Interpolating numbers¶
type Page = `page-${number}`
Valid:
let p: Page = "page-1"
Invalid:
p = "page-one" // ❌
Enforcing Naming Conventions¶
Template literal types can enforce naming rules at compile time.
Enforce camelCase¶
type CamelCaseKey = `${Lowercase<string>}${string}`
Or more strictly:
type CamelCase<T extends string> =
T extends `${infer First}_${infer Rest}`
? `${First}${Capitalize<CamelCase<Rest>>}`
: T
Usage:
type C = CamelCase<"user_name"> // "userName"
This is how ORMs and API clients generate typed field names.
Enforce snake_case¶
type SnakeCase<T extends string> =
T extends `${infer First}${infer Rest}`
? First extends Lowercase<First>
? `${First}${SnakeCase<Rest>}`
: `_${Lowercase<First>}${SnakeCase<Rest>}`
: T
Usage:
type S = SnakeCase<"userName"> // "user_name"
Restrict allowed formats¶
type KebabCase = `${Lowercase<string>}-${Lowercase<string>}`
Valid:
let k: KebabCase = "user-name"
Invalid:
k = "User-Name" // ❌ uppercase not allowed
Real‑World Example: Typed API Routes¶
type Resource = "users" | "posts" | "comments"
type Route = `/${Resource}/${number}`
Usage:
let r: Route
r = "/users/1" // ✔
r = "/posts/42" // ✔
r = "/likes/10" // ❌ "likes" not allowed
This is how frameworks like tRPC and Remix type their route systems.
Real‑World Example: Typed Event Emitters¶
type Events = {
"user:created": { id: string }
"user:deleted": { id: string }
}
type EventName = keyof Events
You can enforce event naming conventions:
type EventPrefix = `user:${"created" | "deleted"}`
This prevents typos like "user:create".
Real‑World Example: Typed CSS Utility Classes¶
type Spacing = 0 | 1 | 2 | 3 | 4
type Direction = "top" | "bottom" | "left" | "right"
type MarginClass = `m-${Direction}-${Spacing}`
Usage:
let m: MarginClass
m = "m-top-2" // ✔
m = "m-left-10" // ❌ 10 not allowed
m = "margin-top" // ❌ wrong format
This is how Tailwind‑style systems can be typed.
Combining Template Literals with Conditional Types¶
Template literal types become extremely powerful when combined with conditional types.
Example: Extract prefix¶
type Prefix<T> =
T extends `${infer P}-${string}` ? P : never
type A = Prefix<"user-created"> // "user"
Example: Extract suffix¶
type Suffix<T> =
T extends `${string}-${infer S}` ? S : never
type B = Suffix<"user-created"> // "created"
This is how you build type‑safe event parsing, routing, and logging systems.
Putting It All Together¶
Here’s a realistic example:
Generate getter method names from object keys.
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
Usage:
type User = { id: string; name: string }
type UserGetters = Getters<User>
// {
// getId: () => string
// getName: () => string
// }
This combines:
- template literal types
- key remapping
- capitalization helpers
This is the foundation of many advanced libraries.
Summary¶
In this lesson, you learned how template literal types let you build expressive, pattern‑based string types:
1. Building string‑based types¶
- combine unions
- prefix/suffix keys
- interpolate numbers
- generate dynamic string unions
2. Enforcing naming conventions¶
- camelCase
- snake_case
- kebab-case
- event naming patterns
- API route formats
Template literal types unlock a new dimension of TypeScript’s type system—letting you model string patterns with the same precision you model objects and functions.