Literal Types & Template Literal Types
Quick Overview
A literal type accepts exactly one value, so `"GET"` is narrower than `string`. Template literal types compose them, letting `` `on${Capitalize<K>}` `` generate every handler name from an existing type.
Deep Dive
1. Pattern-Based Types
TypeScript allows you to manipulate literal types using string template literal syntax directly in the type system.
2. String Template Literal Types
Just as template literals construct strings at runtime, Template Literal Types construct string types at compile time:
type EventType = "click" | "hover" | "focus";
type Component = "button" | "input" | "card";
// Automatically generates 9 literal string combinations!
type DOMEventName = `${Component}:${EventType}`;
// Inferred as: "button:click" | "button:hover" | "input:click" | ...
const handleEvent = (event: DOMEventName) => {
console.log("Handling:", event);
};
handleEvent("button:click"); // Valid!
// handleEvent("header:click"); // Error: Type "header:click" is not assignable
3. Advanced Pattern Matching (API Endpoints)
Use template literal types to enforce API route paths or CSS spacing tokens:
type HTTPMethod = "GET" | "POST";
type APIVersion = "v1" | "v2";
type Endpoint = `/api/${APIVersion}/${string}`;
const validRoute: Endpoint = "/api/v1/users";
// const badRoute: Endpoint = "/v1/users"; // Error: Must start with /api/
4. Built-in String Utility Functions
TypeScript provides built-in intrinsic string manipulation type utilities:
Uppercase<StringType>Lowercase<StringType>Capitalize<StringType>Uncapitalize<StringType>
type Action = "create" | "update";
type GetterName = `get${Capitalize<Action>}`; // "getCreate" | "getUpdate"
5. Common Mistakes
- Combinatorial Explosion: Creating template literal unions from large arrays can produce thousands of string type permutations, slowing down compiler performance.
6. Next Steps
Use template literal types for design system tokens and API route validation. Next, let's explore Type Assertions and as unknown as T!
Next Up in TypeScript & Type System Mechanics
TypeScript Primitives & Explicit Type Annotations
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
Type Inference & Literal Types (const vs. let)
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
Arrays, Tuples & Readonly Modifiers
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.
