TypeScript Template Literal Types

Template literal types are built on string literal types and support generating new string types through interpolation.

This allows TypeScript to perform more precise type checking on strings, suitable for scenarios such as event names, paths, and class names.


SVG Diagram: Template Literal Types Background Title How Template Literal Types Work Input Type Input Type type Event = "click" | "focus" type Method = "get" | "post" Arrow Template Template Literal Template Literal Type type Name = `on${Capitalize}` type Path = `${Method}:/${string}` Arrow Output Type Output Type "onClick" "onFocus" "get:/..." Lower Half: Built-in Utility Types Built-in Utility Types Uppercase "hello" → "HELLO" Lowercase "HELLO" → "hello" Capitalize "hello" → "Hello" Uncapitalize "Hello" → "hello" Arrow Markers

Why Do We Need Template Literal Types

In development, we often need to handle formatted strings, such as event names (onClick), API paths (get:/users), CSS class names (btn-primary-md), and so on.

The plain string type cannot precisely describe these formats, but template literal types allow us to precisely define the types of these strings.

This greatly enhances TypeScript's type safety and reduces runtime errors.

Concept Explanation:Template literal types use backticks (`` ` ``) and${}interpolation syntax to define string types, similar to JavaScript template strings, but used at the type level.


Basic Syntax

Template literal types use backticks and interpolation to define types.

Example

// Define a basic string literal type
type World = "world";

// Use a template literal type
// `Hello ${World}` is equivalent to "Hello world"
type Greeting = `Hello ${World}`;

// Can only assign strings that match the type definition
var greeting: Greeting = "Hello world";
console.log("Greeting: " + greeting);

Output:

问候: Hello world

Explanation:Template literal types replace the interpolation position with the actual string, generating a new literal type.


Built-in Utility Types

TypeScript provides four built-in utility types for handling string casing.

Example

// Uppercase: Converts a string to uppercase
type UpperHello = Uppercase<"hello">;  // "HELLO"

// Lowercase: Converts a string to lowercase
type LowerHELLO = Lowercase<"HELLO">;  // "hello"

// Capitalize: Capitalizes the first letter of a string
type CapitalizedHello = Capitalize<"hello">;  // "Hello"

// Uncapitalize: Lowercases the first letter of a string
type UncapitalizedHello = Uncapitalize<"Hello">;  // "hello"

console.log("Uppercase: " + UpperHello);
console.log("Lowercase: " + LowerHELLO);
console.log("Capitalize: " + CapitalizedHello);
console.log("Uncapitalize: " + UncapitalizedHello);

Output:

Uppercase: HELLO
Lowercase: hello
Capitalize: Hello
Uncapitalize: hello

Use Cases:These utility types are very useful in scenarios that require unified formatting, such as event names and method names.


Event Types

Template literal types can be used to precisely define the types of event names.

Example

// Build event name types
// `on${Capitalize<string>}` generates strings starting with "on" and capitalized first letter
type EventName = `on${Capitalize<string>}`;
// `handle${Capitalize<string>}` generates strings starting with "handle" and capitalized first letter
type Handler = `handle${Capitalize<string>}`;

// Can only assign strings that match the format
var clickEvent: EventName = "onClick";
var focusEvent: EventName = "onFocus";
var handler: Handler = "handleSubmit";

console.log("Event: " + clickEvent);
console.log("Handler: " + handler);

Output:

事件: onClick
处理器: handleSubmit

Advantages:With template literal types, incorrect formats like "onclick" (lowercase) will be rejected by TypeScript.


Path Types

Template literal types can be used to precisely define the types of API paths.

Example

// Define HTTP method types
type HttpMethod = "get" | "post" | "put" | "delete";

// Define path format
type ApiEndpoint = `/${string}`;  // Strings starting with a slash

// Combine into a complete API path type
type ApiPath = `${HttpMethod}${ApiEndpoint}`;

// Can only assign paths that match the format
var getUsers: ApiPath = "/get/users";
var createUser: ApiPath = "/post/users";

console.log("Path: " + getUsers);
console.log("Path: " + createUser);

Output:

路径: /get/users
路径: /post/users

Type Safety:Paths like "/users" (without a method prefix) will be reported as errors by TypeScript.


Complex Example

Template literal types can combine multiple union types to generate all possible combinations.

Example

// Template type with numbers
// ${number} matches any number
type Row = `row${number}`;
type Row10 = Row;  // row0, row1, row2... up to row9...

// Combine multiple types
type Variant = "primary" | "secondary";
type Size = "sm" | "md" | "lg";
// This generates 6 combinations: btn-primary-sm, btn-primary-md, btn-primary-lg...
type ClassName = `btn-${Variant}-${Size}`;

// Can only assign one of the 6 generated combinations
var className: ClassName = "btn-primary-md";
console.log("Class name: " + className);

Output:

类名: btn-primary-md

Combination Explosion:Template literal types automatically expand all combinations. If the union types have many options, the generated type can become very large.


Custom Utility Types

You can create your own template literal utility types.

Example

// Utility type for adding a prefix
// T is any string, P is the prefix to add
type Prefix<T extends string, P extends string> = `${P}${Capitalize<T>}`;

// Utility type for adding a suffix
// T is any string, S is the suffix to add
type Suffix<T extends string, S extends string> = `${Capitalize<T>}${S}`;

// Use custom utility types
type HandlerName = Prefix<"click", "on">;
type ButtonId = Suffix<"submit", "Btn">;

var handler: HandlerName = "onClick";
var id: ButtonId = "SubmitBtn";

console.log("Handler: " + handler);
console.log("ID: " + id);

Output:

处理器: onClick
ID: SubmitBtn

Generic Templates:Template literal types can be combined with generics to create reusable utility types.


Notes

  • Interpolation Types:In a template, the${}can be specific strings, union types, string, number, etc.
  • Number of Combinations:When combining multiple union types, the generated type can be very large
  • Case Handling:Use built-in utility types to handle string casing

Best Practices:Using template literal types to handle strings with fixed formats such as event names, paths, and class names can achieve better type safety.


Summary

Template literal types are part of TypeScript's powerful type system.

  • Template Syntax:Use${T}interpolation to build types
  • Built-in Utilities:Uppercase、Lowercase、Capitalize、Uncapitalize
  • Use Cases:Event names, API paths, CSS class names, etc.
  • Customization:Create reusable utility types

Recommendation:In scenarios requiring formatted strings, prioritize using template literal types to obtain compile-time type checking.


Other Extensions