Difficulty: Advanced
What are template literal types in TypeScript? How can you use them to create powerful string-based type patterns?
Template literal types use the same backtick syntax as JavaScript template literals but at the type level. They let you construct new string literal types by concatenating other string literals, unions, and type variables.
Combined with union types, they generate every permutation automatically. TypeScript also provides built-in string manipulation types: Uppercase, Lowercase, Capitalize, and Uncapitalize.
Template literal types are heavily used in library typings (e.g., event emitters, CSS-in-JS, routing) to provide autocomplete and type safety for string-based APIs.
// Simple concatenation
type Greeting = `Hello, ${string}`;
const a: Greeting = 'Hello, Alice'; // OK
// const b: Greeting = 'Hi, Alice'; // Error
// Union expansion - generates all permutations
type Color = 'red' | 'green' | 'blue';
type Shade = 'light' | 'dark';
type ColorVariant = `${Shade}-${Color}`;
// 'light-red' | 'light-green' | 'light-blue'
// | 'dark-red' | 'dark-green' | 'dark-blue'
// CSS property pattern
type CSSUnit = 'px' | 'em' | 'rem' | '%' | 'vh' | 'vw';
type CSSValue = `${number}${CSSUnit}`;
const width: CSSValue = '100px'; // OK
const height: CSSValue = '50vh'; // OK
// const bad: CSSValue = '10feet'; // Error
Template literal types combine string parts at the type level. When unions are involved, TypeScript generates every possible combination automatically.
// Type-safe event emitter using template literals
type EventName<T> = {
[K in keyof T & string as `on${Capitalize<K>}`]: (value: T[K]) => void;
};
interface FormFields {
name: string;
email: string;
age: number;
}
type FormEvents = EventName<FormFields>;
// {
// onName: (value: string) => void;
// onEmail: (value: string) => void;
// onAge: (value: number) => void;
// }
// Getter/Setter pattern
type Accessors<T> = {
[K in keyof T & string as `get${Capitalize<K>}`]: () => T[K];
} & {
[K in keyof T & string as `set${Capitalize<K>}`]: (val: T[K]) => void;
};
type UserAccessors = Accessors<{ name: string; age: number }>;
// { getName: () => string; getAge: () => number;
// setName: (val: string) => void; setAge: (val: number) => void; }
Template literal types with mapped types let you generate method names from property names. This is how libraries provide type-safe APIs for string-based patterns.
// Extract parts from a string type
type ExtractRoute<S extends string> =
S extends `${infer Start}/:${infer Param}/${infer Rest}`
? Param | ExtractRoute<`/${Rest}`>
: S extends `${infer Start}/:${infer Param}`
? Param
: never;
type Params = ExtractRoute<'/users/:userId/posts/:postId'>;
// 'userId' | 'postId'
// Built-in string manipulation types
type Upper = Uppercase<'hello'>; // 'HELLO'
type Lower = Lowercase<'HELLO'>; // 'hello'
type Cap = Capitalize<'hello'>; // 'Hello'
type Uncap = Uncapitalize<'Hello'>; // 'hello'
// Dot-notation path type
type DotPath<T, Prefix extends string = ''> = {
[K in keyof T & string]: T[K] extends object
? DotPath<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
}[keyof T & string];
interface Config {
db: { host: string; port: number };
app: { name: string };
}
type ConfigPaths = DotPath<Config>;
// 'db.host' | 'db.port' | 'app.name'
Combining template literals with infer lets you parse and decompose string types. This is the foundation for type-safe routing, dot-path accessors, and string-based DSLs.
Template Literal Types, String Manipulation Types, Type-Level String Parsing, Pattern Matching