Chapter 30: TypeScript Literal Types
Literal Types in TypeScript very calmly and step by step, like we’re sitting together with VS Code open, chai on the table, and we’re typing small examples one by one until everything feels crystal clear.
Literal types are one of the features that make TypeScript feel magical when used correctly — they turn vague string / number into very precise, almost compile-time constants.
1. What exactly is a literal type? (the simplest honest explanation)
A literal type is a type that represents exactly one concrete value — not a whole category.
|
0 1 2 3 4 5 6 7 8 |
let direction: "up" // ← this is a literal type: only "up" is allowed let statusCode: 200 // ← only the number 200 is allowed let isSuccess: true // ← only the value true is allowed |
Compare with normal types:
|
0 1 2 3 4 5 6 7 |
let anyDirection: string // any string is okay → "up", "down", "left", "banana" let anyCode: number // 200, 404, 3.14, -5, everything goes |
So literal types are extremely narrow — they describe one single possible value.
2. Most common and useful form: string literal union
Almost everyone first meets literal types as unions of string literals:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
type Direction = "up" | "down" | "left" | "right"; let move: Direction; // move = "forward"; → ❌ Error — "forward" is not part of the union move = "up"; // ✅ OK move = "down"; // ✅ OK |
This pattern is extremely common in 2025–2026 code:
- button variants
- API status/response kinds
- event names
- theme modes
- user roles
- HTTP methods
- form input types
- Tailwind/size/color enums
Realistic everyday example:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
type AlertVariant = "success" | "error" | "warning" | "info"; interface AlertProps { variant: AlertVariant; message: string; } function Alert({ variant, message }: AlertProps) { const colors = { success: "bg-green-100 text-green-800", error: "bg-red-100 text-red-800", warning: "bg-yellow-100 text-yellow-800", info: "bg-blue-100 text-blue-800", }; return ( <div className={`p-4 rounded {colors[variant]}`}> {message} </div> ); } // Usage — autocomplete + safety <Alert variant="success" message="Saved!" /> // <Alert variant="danger" message="Oops" /> → red squiggle immediately |
3. Literal types exist for string, number, boolean, bigint
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// String literals type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; // Number literals type StatusCode = 200 | 201 | 400 | 401 | 403 | 404 | 500; // Boolean literals type IsActive = true; // only true is allowed type IsLoading = false; // only false // BigInt literals (less common) type BigId = 1234567890123456789n; |
4. Literal types + as const — the most powerful daily combo
When you write an object/array literal, TypeScript normally widens the values:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
const settings = { theme: "dark", language: "en", debug: true }; // settings.theme → string (not "dark") // settings.language → string // settings.debug → boolean |
But very often you want literal narrowness (especially for configs, routes, enums-like objects, theme tokens).
Solution → as const
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const settings = { theme: "dark", language: "en", debug: true } as const; // Now: settings.theme // "dark" ← literal type! settings.language // "en" settings.debug // true // And object becomes readonly // settings.theme = "light"; → error — readonly |
Even better — combine with type-level extraction:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
const roles = { admin: "ADMIN", editor: "EDITOR", viewer: "VIEWER" } as const; type Role = keyof typeof roles; // "admin" | "editor" | "viewer" type RoleValue = (typeof roles)[Role]; // "ADMIN" | "EDITOR" | "VIEWER" |
This pattern (const object + as const + keyof typeof) replaced most enum usages after ~2020.
5. Very common real patterns in 2025–2026 codebases
Pattern 1: Status / result kinds (discriminated unions)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type Result<T> = | { kind: "loading" } | { kind: "success"; data: T } | { kind: "error"; message: string }; function display(result: Result<User>) { if (result.kind === "success") { // result.data → User (narrowed) } } |
Pattern 2: Route / path literals
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type AppRoute = | "/dashboard" | "/users" | "/users/:id" | "/settings/profile" | "/settings/billing"; function navigate(to: AppRoute) { // safe — only valid routes allowed } |
Pattern 3: Theme / design tokens
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const colors = { primary: "#3b82f6", primaryHover: "#2563eb", danger: "#ef4444", success: "#10b981" } as const; type ColorKey = keyof typeof colors; type ColorValue = (typeof colors)[ColorKey]; |
Pattern 4: Button / component variants
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
type ButtonVariant = "solid" | "outline" | "ghost" | "link"; type ButtonSize = "sm" | "md" | "lg"; interface ButtonProps { variant?: ButtonVariant; size?: ButtonSize; children: React.ReactNode; } |
6. Quick summary table — Literal types cheat sheet
| Kind | Example syntax | Widened without as const | Narrow with as const | Most common use-case |
|---|---|---|---|---|
| String literal | “loading” | string | “loading” | Status, variants, events, roles |
| Number literal | 200 | number | 200 | Status codes, magic numbers (rare) |
| Boolean literal | true | boolean | true | Flags that should only be one way |
| Object literal | { status: “ok” } as const | { status: string } | { readonly status: “ok” } | Configs, constants, theme objects |
| Array literal | [“left”, “right”] as const | string[] | readonly [“left”, “right”] | Fixed choices, directions, days of week |
| Union of literals | “GET” | “POST” | “PUT” | — | — | Almost every enum-like value in modern TS |
Your mini homework (try right now in playground)
- Create type Size = “xs” | “sm” | “md” | “lg” | “xl”
- Make a const sizes = { xs: “0.75rem”, sm: “0.875rem”, … } as const
- Extract type SizeKey = keyof typeof sizes
- Extract type SizeValue = (typeof sizes)[SizeKey]
- Try assigning wrong value → watch the beautiful red underline
Which part feels most useful for your current project?
Want to go deeper into:
- as const vs traditional enum
- Literal types in React props / Tailwind / shadcn/ui
- Combining literal unions with discriminated unions
- Literal types in generics / conditional types / mapped types
- Common mistakes people still make with literals
Just tell me — we’ll zoom in right there! 😄
