TypeScript Union and Intersection Types: Practical Examples
TypeScriptAdvancedType Safety

TypeScript Union and Intersection Types: Practical Examples

Published 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)

typescript
type ID = string | number;

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

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

Discriminated Unions

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

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

Real-World: Loading States

typescript
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)

typescript
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

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

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

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

type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;

Complex Example: Form Fields

typescript
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 `<input type="text" />`;
    case "number":
      return `<input type="number" />`;
  }
}

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!

Frontend Strategy

Need a frontend roadmap that supports growth, not churn?

I help product and marketing teams simplify stack decisions, reduce frontend drag, and ship faster without creating long-term maintenance debt.

S

Written by Salman Izhar

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

Learn More