Create types from string templates (TypeScript 4.1+).

Basic Template Literals

type Greeting = `Hello, ${string}`;
// "Hello, " + any string

type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">;
// "onClick"

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiEndpoint = `/${string}`;
type ApiRoute = `${HttpMethod} ${ApiEndpoint}`;

type Route = "GET /users" | "POST /users" | "GET /posts";

String Manipulation Types

Uppercase, Lowercase, Capitalize, Uncapitalize

type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;

type T0 = Uppercase<"hello">;      // "HELLO"
type T1 = Lowercase<"WORLD">;      // "world"
type T2 = Capitalize<"hello">;      // "Hello"
type T3 = Uncapitalize<"Hello">;   // "hello"

Pattern Matching

type ExtractRoute<T extends string> = T extends `${infer Method} ${infer Path}`
  ? { method: Method; path: Path }
  : never;

type Route = ExtractRoute<"GET /users">;
// { method: "GET"; path: "/users" }

Extract Parts

type ExtractMethod<T extends string> = T extends `${infer M} ${string}`
  ? M
  : never;

type ExtractPath<T extends string> = T extends `${string} ${infer P}`
  ? P
  : never;

type Method = ExtractMethod<"GET /users">; // "GET"
type Path = ExtractPath<"GET /users">;     // "/users"

CSS Class Names

type Color = "red" | "blue" | "green";
type Size = "small" | "medium" | "large";
type ButtonClass = `btn-${Color}-${Size}`;

type Classes =
  | "btn-red-small"
  | "btn-red-medium"
  | "btn-red-large"
  | "btn-blue-small"
  | // ... etc

API Route Types

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
type Resource = "users" | "posts" | "comments";
type IdParam = `${Resource}/${string}`;
type ApiRoute = `${HttpMethod} /api/${IdParam}`;

type Routes =
  | "GET /api/users/123"
  | "POST /api/posts"
  | "PUT /api/comments/456";

Path Parsing

type Split<S extends string, D extends string> = S extends `${infer Head}${D}${infer Tail}`
  ? [Head, ...Split<Tail, D>]
  : [S];

type PathParts = Split<"a/b/c", "/">;
// ["a", "b", "c"]

Exercises

Exercise 1: Event Handlers

// Create a type for event handler names:
// - Prefix: "on"
// - Event: "click" | "hover" | "focus"
// - Result: "onClick" | "onHover" | "onFocus"

Exercise 2: CSS Classes

// Create a type for button classes:
// - Base: "btn"
// - Variant: "primary" | "secondary"
// - Size: "sm" | "md" | "lg"
// - Result: "btn-primary-sm" | "btn-primary-md" | etc.

Exercise 3: API Routes

// Create a type for API routes:
// - Method: "GET" | "POST"
// - Resource: "users" | "posts"
// - Result: "GET /api/users" | "POST /api/posts" | etc.

Exercise 4: Path Extraction

// Create a type that extracts:
// - The last segment from a path
// - Example: "a/b/c" => "c"

Exercise 5: String Replacement

// Create a type that replaces:
// - All occurrences of a substring
// - Example: Replace<"hello-world", "-", "_"> => "hello_world"

Best Practices

  1. Use for string patterns - When you have known string formats
  2. Combine with unions - Create type-safe string literals
  3. Extract with infer - Parse strings into structured types
  4. Use built-in utilities - Capitalize, Uppercase, etc.
  5. Test with real values - Ensure types work as expected

Common Mistakes

  1. Not using extends string
// ❌ Wrong
type Bad<T> = `Hello, ${T}`;

// ✅ Correct
type Good<T extends string> = `Hello, ${T}`;
  1. Complex template literals
// ❌ Too complex, might hit limits
type VeryComplex = `${A}${B}${C}${D}${E}${F}...`;

// ✅ Keep it simple
type Simple = `${A}-${B}`;
  1. Not handling edge cases
// ❌ Might not handle empty strings
type Bad<T extends string> = `prefix-${T}`;

// ✅ Consider edge cases
type Good<T extends string> = T extends "" ? "prefix" : `prefix-${T}`;

Next Steps

Learn about Recursive Types.