TypeScript Union and Intersection Types: Practical Examples
TypeScriptAdvancedType Safety

TypeScript Union and Intersection Types: Practical Examples

January 17, 2025
11 min read
Salman Izhar

TypeScript Union and Intersection Types

Master union and intersection types for flexible, type-safe code.

Union Types (OR relationship)

type ID = string | number;

function printID(id: ID) {
  console.log(`ID: ${id}`);
}

printID(101);       // ✅ OK
printID("ABC123");  // ✅ OK

Discriminated Unions

type Result =
  | { success: true; data: T }
  | { success: false; error: string };

function handleResult(result: Result) {
  if (result.success) {
    console.log(result.data);
  } else {
    console.log(result.error);
  }
}

Real-World: Loading States

type LoadingState =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: User[] }
  | { status: "error"; error: Error };

function renderUsers(state: LoadingState) {
  switch (state.status) {
    case "idle":
      return "Click to load";
    case "loading":
      return "Loading...";
    case "success":
      return state.data.map(u => u.name).join(", ");
    case "error":
      return `Error: ${state.error.message}`;
  }
}

Intersection Types (AND relationship)

interface Person {
  name: string;
  age: number;
}

interface Employee {
  employeeId: string;
  department: string;
}

type Staff = Person & Employee;

const employee: Staff = {
  name: "Alice",
  age: 30,
  employeeId: "E123",
  department: "Engineering"
};

Real-World: API Response

interface BaseResponse {
  timestamp: Date;
  requestId: string;
}

interface SuccessResponse extends BaseResponse {
  success: true;
  data: T;
}

interface ErrorResponse extends BaseResponse {
  success: false;
  error: { code: string; message: string };
}

type ApiResponse = SuccessResponse | ErrorResponse;

Complex Example: Form Fields

interface BaseField {
  name: string;
  label: string;
}

interface TextField extends BaseField {
  type: "text";
  maxLength?: number;
}

interface NumberField extends BaseField {
  type: "number";
  min?: number;
  max?: number;
}

type FormField = TextField | NumberField;

function renderField(field: FormField) {
  switch (field.type) {
    case "text":
      return ``;
    case "number":
      return ``;
  }
}

Quick Reference

| Pattern | Symbol | Example | |---------|--------|---------| | Union | | | string | number | | Intersection | & | Person & Employee |

Best Practices

1. Use unions for "or" relationships 2. Use intersections for "and" relationships 3. Prefer discriminated unions for state 4. Add type guards for narrowing

Conclusion

  • Union Types: Value can be one of several types
  • Intersection Types: Combine multiple types
  • Discriminated Unions: Type-safe state machines

Master these for robust TypeScript!

Get More Like This

Want articles like this in your inbox?

Join developers and founders who get practical insights on frontend, SaaS, and building better products.

S

Written by Salman Izhar

Frontend Developer specializing in React, Next.js, and building high-converting web applications.

Learn More