SyntaxStudy
Sign Up
TypeScript Beginner 1 min read

Template Literal Type Aliases

Template literal types, introduced in TypeScript 4.1, allow you to build new string literal types by combining existing ones using template literal syntax. They mirror JavaScript template literals in syntax but operate entirely at the type level. This enables you to create precise types for string patterns, property names, and event names. When template literal types are combined with union types, TypeScript automatically distributes over every combination to produce a union of all possible resulting strings. For example, combining a union of three strings with another union of two strings produces a union of six string types. This makes it easy to define types for all valid combinations of a set of options. Template literal types pair especially well with mapped types and the built-in string manipulation types (Uppercase, Lowercase, Capitalize, Uncapitalize) to produce sophisticated type transformations. A common use case is generating event handler property names from event name strings, or building getter and setter name pairs from a set of field names.
Example
// Basic template literal type
type Greeting = `Hello, ${string}!`;
const g: Greeting = "Hello, World!"; // OK

// Combining unions — cartesian product
type Color  = "red" | "green" | "blue";
type Shade  = "light" | "dark";
type Swatch = `${Shade}-${Color}`;
// "light-red" | "light-green" | "light-blue"
// | "dark-red"  | "dark-green"  | "dark-blue"

const s: Swatch = "dark-blue";

// Generate event handler names
type EventName = "click" | "focus" | "blur";
type Handler   = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"

// Typed CSS property access
type CSSProperty   = "margin" | "padding";
type CSSDirection  = "Top" | "Right" | "Bottom" | "Left";
type CSSLonghand   = `${CSSProperty}${CSSDirection}`;
// "marginTop" | "marginRight" | ... | "paddingLeft"

// Extract prefix from a string type
type RoutePrefix<T extends string> = T extends `/${infer Rest}` ? Rest : T;
type Route = "/users" | "/posts" | "home";
type Unprefixed = RoutePrefix<Route>; // "users" | "posts" | "home"

// Getter/setter pairs
type GetterSetter<T extends string> = {
    [K in T as `get${Capitalize<K>}`]: () => string;
} & {
    [K in T as `set${Capitalize<K>}`]: (v: string) => void;
};

type NamedField = GetterSetter<"name" | "email">;
// { getName: () => string; setName: (v) => void;
//   getEmail: () => string; setEmail: (v) => void }