+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 23 of 355

๐ŸŽฏ Literal Types: Exact Value Types in TypeScript

Master literal types in TypeScript to create precise, type-safe APIs with exact string, number, and boolean values ๐Ÿš€

๐Ÿš€Intermediate
20 min read

Prerequisites

  • Understanding of basic TypeScript types ๐Ÿ“
  • Knowledge of union types โšก
  • Familiarity with const assertions ๐Ÿ’ป

What you'll learn

  • Create precise literal types ๐ŸŽฏ
  • Combine literals with unions ๐Ÿ—๏ธ
  • Use template literal types ๐Ÿ”
  • Build type-safe APIs with literals โœจ

๐ŸŽฏ Introduction

Welcome to the precise world of literal types in TypeScript! ๐ŸŽ‰ In this guide, weโ€™ll explore how to use exact values as types, creating incredibly specific and type-safe APIs.

Think of literal types as the difference between saying โ€œany numberโ€ and โ€œexactly 42โ€ ๐ŸŽฒ. Instead of accepting any string, you can demand exactly โ€œsuccessโ€ or โ€œerrorโ€. Itโ€™s like having a bouncer at a club who only lets in people with specific names on the guest list! ๐Ÿ“‹

By the end of this tutorial, youโ€™ll be creating APIs so precise that bugs will have nowhere to hide! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Literal Types

๐Ÿค” What Are Literal Types?

Literal types are like VIP passes ๐ŸŽซ - they represent exact, specific values rather than broad categories. Instead of string, you can have "hello". Instead of number, you can have 42.

// Regular types (broad)
let status: string = "anything goes";
let count: number = 999;

// Literal types (specific)
let status: "success" = "success"; // Only "success" allowed!
let count: 42 = 42;                // Only 42 allowed!

๐Ÿ’ก Types of Literals

TypeScript supports several literal types:

  1. String Literals ๐Ÿ“: Exact string values
  2. Number Literals ๐Ÿ”ข: Exact numeric values
  3. Boolean Literals โœ…: true or false (yes, theyโ€™re literals!)
  4. Template Literals ๐ŸŽจ: String patterns (advanced)

๐ŸŽฏ Why Use Literal Types?

Hereโ€™s why literal types are game-changers:

  1. Precise APIs ๐ŸŽฏ: Accept only specific values
  2. Better IntelliSense ๐Ÿ’ป: IDE knows exact options
  3. Compile-Time Safety ๐Ÿ›ก๏ธ: Catch typos immediately
  4. Self-Documenting ๐Ÿ“–: Code shows exact values allowed
  5. Discriminated Unions ๐Ÿ”„: Pattern matching with literals

Real-world example: HTTP methods ๐ŸŒ - you want โ€œGETโ€, โ€œPOSTโ€, โ€œPUTโ€, not any random string!

๐Ÿ”ง Basic Literal Types

๐Ÿ“ String Literals

Letโ€™s start with string literals - the most common type:

// ๐ŸŽฏ Simple string literal
type Direction = "north" | "south" | "east" | "west";

function move(direction: Direction) {
  console.log(`Moving ${direction}! ๐Ÿงญ`);
}

move("north");  // โœ… Works!
move("south");  // โœ… Works!
// move("up");  // โŒ Error! "up" is not assignable to Direction

// ๐ŸŽจ Real-world example: Status codes
type Status = "pending" | "approved" | "rejected";

interface Order {
  id: number;
  status: Status;
}

const order: Order = {
  id: 123,
  status: "approved" // โœ… IntelliSense shows all options!
};

// ๐Ÿš€ Configuration options
type LogLevel = "debug" | "info" | "warn" | "error";

function log(message: string, level: LogLevel) {
  const icons = {
    debug: "๐Ÿ”",
    info: "โ„น๏ธ",
    warn: "โš ๏ธ",
    error: "โŒ"
  };
  
  console.log(`${icons[level]} [${level.toUpperCase()}] ${message}`);
}

log("Application started", "info");
log("Something went wrong", "error");

๐Ÿ”ข Number Literals

Number literals for exact numeric values:

// ๐ŸŽฒ Dice rolls
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function rollDice(): DiceRoll {
  return Math.floor(Math.random() * 6) + 1 as DiceRoll;
}

// ๐Ÿ“Š HTTP status codes
type SuccessCode = 200 | 201 | 204;
type ErrorCode = 400 | 404 | 500;
type StatusCode = SuccessCode | ErrorCode;

function handleResponse(code: StatusCode) {
  switch (code) {
    case 200:
      console.log("โœ… OK");
      break;
    case 404:
      console.log("โŒ Not Found");
      break;
    // TypeScript ensures all cases handled!
  }
}

// ๐ŸŽฎ Game difficulty levels
type Difficulty = 1 | 2 | 3;

interface GameSettings {
  difficulty: Difficulty;
  playerName: string;
}

const settings: GameSettings = {
  difficulty: 2, // Medium
  playerName: "Player1"
};

โœ… Boolean Literals

Even booleans can be literals:

// ๐Ÿ“ฑ Feature flags
type FeatureEnabled = true;
type FeatureDisabled = false;

interface Features {
  darkMode: boolean;              // Can be true or false
  betaFeature: FeatureEnabled;    // Must be true
  deprecatedAPI: FeatureDisabled; // Must be false
}

// ๐Ÿ” Strict configurations
type StrictMode = true; // Always strict!

interface CompilerOptions {
  strict: StrictMode;
  sourceMap: boolean;
}

const tsConfig: CompilerOptions = {
  strict: true,      // Must be true
  sourceMap: false   // Can be either
};

๐ŸŒˆ Combining Literal Types

๐ŸŽฏ Union of Literals

The real power comes from combining literals:

// ๐ŸŽจ Theme system
type ColorScheme = "light" | "dark" | "auto";
type ThemeColor = "blue" | "green" | "purple" | "orange";

interface Theme {
  scheme: ColorScheme;
  primaryColor: ThemeColor;
}

// ๐Ÿ—๏ธ Complex state machines
type LoadingState = {
  status: "loading";
};

type SuccessState = {
  status: "success";
  data: string;
};

type ErrorState = {
  status: "error";
  message: string;
};

type State = LoadingState | SuccessState | ErrorState;

function handleState(state: State) {
  switch (state.status) {
    case "loading":
      return "โณ Loading...";
    case "success":
      return `โœ… ${state.data}`;
    case "error":
      return `โŒ ${state.message}`;
    // Exhaustive - all cases covered!
  }
}

// ๐ŸŽฎ Game actions
type PlayerAction = 
  | { type: "move"; direction: Direction }
  | { type: "attack"; target: string }
  | { type: "defend" }
  | { type: "useItem"; item: string };

function handleAction(action: PlayerAction) {
  switch (action.type) {
    case "move":
      console.log(`Moving ${action.direction} ๐Ÿƒ`);
      break;
    case "attack":
      console.log(`Attacking ${action.target} โš”๏ธ`);
      break;
    case "defend":
      console.log("Defending ๐Ÿ›ก๏ธ");
      break;
    case "useItem":
      console.log(`Using ${action.item} ๐Ÿงช`);
      break;
  }
}

๐Ÿ”„ Literal Type Narrowing

TypeScript automatically narrows literal types:

// ๐ŸŽฏ Const assertions create literal types
const config = {
  mode: "production",
  port: 3000
} as const;

// Type is: { readonly mode: "production"; readonly port: 3000; }

// ๐ŸŒŸ Array literals
const colors = ["red", "green", "blue"] as const;
// Type is: readonly ["red", "green", "blue"]

type Color = typeof colors[number]; // "red" | "green" | "blue"

// ๐ŸŽจ Function that returns literals
function getStatus(success: boolean) {
  return success ? "ok" : "error";
} // Return type: "ok" | "error"

// ๐Ÿ—๏ธ Building literal types from values
const ROLES = {
  ADMIN: "admin",
  USER: "user",
  GUEST: "guest"
} as const;

type Role = typeof ROLES[keyof typeof ROLES]; // "admin" | "user" | "guest"

๐ŸŽจ Template Literal Types

๐Ÿ“ String Pattern Types

Template literal types let you create string patterns:

// ๐ŸŽฏ Basic template literals
type EventName = `on${string}`;
const click: EventName = "onClick";    // โœ…
const change: EventName = "onChange";  // โœ…
// const invalid: EventName = "click"; // โŒ Must start with "on"

// ๐ŸŽจ Combining literals
type Color = "red" | "green" | "blue";
type Shade = "light" | "dark";
type ColorShade = `${Shade}-${Color}`;
// Type is: "light-red" | "light-green" | "light-blue" | 
//          "dark-red" | "dark-green" | "dark-blue"

// ๐Ÿ—๏ธ CSS units
type CSSUnit = "px" | "em" | "rem" | "%";
type CSSValue = `${number}${CSSUnit}`;

const width: CSSValue = "100px";    // โœ…
const height: CSSValue = "2.5rem";  // โœ…
// const invalid: CSSValue = "100";  // โŒ Missing unit

// ๐ŸŒ URL patterns
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/${string}`;
type APIRoute = `${HTTPMethod} ${APIEndpoint}`;

const route1: APIRoute = "GET /api/users";     // โœ…
const route2: APIRoute = "POST /api/products"; // โœ…
// const invalid: APIRoute = "GET /users";     // โŒ Missing /api/

๐Ÿš€ Advanced Template Patterns

// ๐ŸŽฏ Uppercase/Lowercase transformations
type Greeting<T extends string> = `hello ${Lowercase<T>}`;
type Shout<T extends string> = `${Uppercase<T>}!`;

type HelloWorld = Greeting<"WORLD">;  // "hello world"
type LoudHello = Shout<"hello">;      // "HELLO!"

// ๐Ÿ—๏ธ Event handler names
type EventType = "click" | "change" | "focus" | "blur";
type EventHandler = `on${Capitalize<EventType>}`;
// "onClick" | "onChange" | "onFocus" | "onBlur"

interface ButtonProps {
  onClick?: () => void;
  onFocus?: () => void;
  // Autocomplete knows all valid handlers!
}

// ๐ŸŽจ BEM CSS naming
type Block = "button" | "card" | "modal";
type Element = "header" | "body" | "footer";
type Modifier = "active" | "disabled" | "large";

type BEMClass = 
  | Block
  | `${Block}__${Element}`
  | `${Block}--${Modifier}`
  | `${Block}__${Element}--${Modifier}`;

const class1: BEMClass = "button";                    // โœ…
const class2: BEMClass = "card__header";              // โœ…
const class3: BEMClass = "modal--active";             // โœ…
const class4: BEMClass = "button__body--disabled";    // โœ…

๐Ÿ† Best Practices and Patterns

๐ŸŽฏ Creating Type-Safe APIs

// ๐ŸŒ API client with literal types
type Method = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = 
  | "/users"
  | "/products"
  | "/orders"
  | `/users/${string}`
  | `/products/${string}`;

interface APIRequest {
  method: Method;
  endpoint: Endpoint;
  body?: unknown;
}

class APIClient {
  async request({ method, endpoint, body }: APIRequest) {
    const options: RequestInit = {
      method,
      headers: { "Content-Type": "application/json" },
      body: body ? JSON.stringify(body) : undefined
    };
    
    return fetch(`https://api.example.com${endpoint}`, options);
  }
}

// Usage with full type safety!
const client = new APIClient();
client.request({
  method: "GET",
  endpoint: "/users"
});

// ๐ŸŽฎ Game state machine
type GameState = "menu" | "playing" | "paused" | "gameOver";
type GameAction = 
  | { type: "START" }
  | { type: "PAUSE" }
  | { type: "RESUME" }
  | { type: "END" };

const gameTransitions: Record<GameState, Partial<Record<GameAction["type"], GameState>>> = {
  menu: { START: "playing" },
  playing: { PAUSE: "paused", END: "gameOver" },
  paused: { RESUME: "playing", END: "gameOver" },
  gameOver: { START: "playing" }
};

function transition(state: GameState, action: GameAction): GameState {
  return gameTransitions[state][action.type] ?? state;
}

๐ŸŒŸ Real-World Example: Form Builder

// ๐Ÿ—๏ธ Type-safe form builder with literal types
type FieldType = "text" | "email" | "password" | "number" | "select" | "checkbox";
type ValidationRule = "required" | "email" | "minLength" | "maxLength" | "pattern";

interface FieldConfig<T extends FieldType> {
  type: T;
  name: string;
  label: string;
  placeholder?: string;
  validation?: ValidationRule[];
  options?: T extends "select" ? string[] : never;
  min?: T extends "number" ? number : never;
  max?: T extends "number" ? number : never;
}

// Type-safe field creators
function createTextField(config: FieldConfig<"text">) {
  return { ...config, value: "" };
}

function createSelectField(config: FieldConfig<"select">) {
  if (!config.options) {
    throw new Error("Select field requires options!");
  }
  return { ...config, value: config.options[0] };
}

// Usage
const nameField = createTextField({
  type: "text",
  name: "fullName",
  label: "Full Name",
  validation: ["required", "minLength"]
});

const countryField = createSelectField({
  type: "select",
  name: "country",
  label: "Country",
  options: ["USA", "Canada", "UK", "Australia"], // Required for select!
  validation: ["required"]
});

// ๐ŸŽจ Form theme configuration
type ColorScheme = "light" | "dark";
type AccentColor = "blue" | "green" | "purple" | "red";
type Size = "sm" | "md" | "lg";

interface FormTheme {
  scheme: ColorScheme;
  accent: AccentColor;
  inputSize: Size;
  buttonSize: Size;
  borderRadius: `${number}px`;
}

const theme: FormTheme = {
  scheme: "light",
  accent: "blue",
  inputSize: "md",
  buttonSize: "lg",
  borderRadius: "4px"
};

๐ŸŽฏ Practice Exercise

Letโ€™s build a type-safe state machine for a traffic light! ๐Ÿšฆ

// ๐Ÿšฆ Your challenge: Create a traffic light system

type LightColor = "red" | "yellow" | "green";
type LightState = 
  | { color: "red"; canGo: false }
  | { color: "yellow"; canGo: false }
  | { color: "green"; canGo: true };

// TODO: Implement the traffic light controller
class TrafficLight {
  private state: LightState = { color: "red", canGo: false };
  
  // TODO: Implement transition logic
  next(): void {
    // Red -> Green -> Yellow -> Red
  }
  
  // TODO: Get current state
  getState(): LightState {
    // Return current state
  }
  
  // TODO: Check if cars can go
  canCarsGo(): boolean {
    // Return based on state
  }
}

// TODO: Create pedestrian crossing
type PedestrianSignal = "walk" | "dont-walk" | "flashing";

interface CrossingState {
  traffic: LightColor;
  pedestrian: PedestrianSignal;
}

// Map traffic lights to pedestrian signals
const crossingLogic: Record<LightColor, PedestrianSignal> = {
  // TODO: Fill in the logic
};

// Test your implementation
const light = new TrafficLight();
console.log(light.getState()); // { color: "red", canGo: false }
light.next();
console.log(light.getState()); // { color: "green", canGo: true }

๐ŸŽ‰ Conclusion

Congratulations! Youโ€™ve mastered literal types in TypeScript! ๐Ÿ† Letโ€™s recap what youโ€™ve learned:

  • ๐Ÿ“ String Literals: Exact string values as types
  • ๐Ÿ”ข Number Literals: Precise numeric types
  • โœ… Boolean Literals: Even true/false can be literal!
  • ๐ŸŽจ Template Literals: Pattern-based string types

Key takeaways:

  • Use literals for precise, self-documenting APIs ๐ŸŽฏ
  • Combine with unions for powerful type systems ๐ŸŒˆ
  • Leverage template literals for string patterns ๐ŸŽจ
  • Let TypeScriptโ€™s inference work for you ๐Ÿค–

Youโ€™re now equipped to create incredibly type-safe APIs where bugs have nowhere to hide! Keep practicing with literal types - theyโ€™re one of TypeScriptโ€™s most powerful features for creating precise, user-friendly APIs! ๐Ÿš€