🇬🇧 Limited Time — UK Only·🎓 Free Learning for 1 Month·🤖 Free AI Training Included·📚 4,000+ Lessons · 35,000+ Quizzes·🏆 GCSE Mocks · Olympiad Papers·⚡ Selected Students Only · Limited Places·🎁 Free Value Worth £2,000·🇬🇧 Limited Time — UK Only·🎓 Free Learning for 1 Month·🤖 Free AI Training Included·📚 4,000+ Lessons · 35,000+ Quizzes·🏆 GCSE Mocks · Olympiad Papers·⚡ Selected Students Only · Limited Places·🎁 Free Value Worth £2,000·🇬🇧 Limited Time — UK Only·🎓 Free Learning for 1 Month·🤖 Free AI Training Included·📚 4,000+ Lessons · 35,000+ Quizzes·🏆 GCSE Mocks · Olympiad Papers·⚡ Selected Students Only · Limited Places·🎁 Free Value Worth £2,000·
📘 TypeScript

Union types and narrowing

A union type is A | B: the value can be A or B. You then narrow (check) which one it is with typeof or checks, so TypeScript knows the type in each branch.

3 min 10 XP Lesson 4 of 21
Union types and narrowing
🌐

Appy Says…

Sometimes a value can be one of several types — a loading state is either 'loading', 'success', or 'error'. TypeScript union types model this exactly, and with narrowing, you can handle each case safely.

📖

What are Union Types?

A union type allows a value to be one of several types, separated by |. TypeScript tracks which possibilities are in play and narrows them as you check.

  • type Status = 'loading' | 'success' | 'error';
  • let result: string | number;
  • Narrowing with typeof: if (typeof x === 'string') { x.toUpperCase(); }
  • Narrowing with ===: if (status === 'error') { showError(); }
  • Discriminated union: objects with a shared type field
  • Exhaustive check: default: const _: never = status; — catches unhandled cases
🎮

Think of it like a Minecraft item that can be a sword OR a bow

An item slot can hold either a sword or a bow. Before using it, you check which it is. Union types work the same — a value could be string or number, and TypeScript requires you to check before using type-specific methods.

⚙️

How It Works

  • 1. Declare: type ID = string | number;
  • 2. TS allows only methods common to all types unless you narrow
  • 3. Narrow with typeof: if (typeof id === 'string') { id.toLowerCase(); }
  • 4. Discriminated union: type Shape = { kind: 'circle'; r: number } | { kind: 'rect'; w: number; h: number };
  • 5. Narrow by discriminant: if (shape.kind === 'circle') { /* r is available */ }
  • 6. Switch exhaustiveness: default: const _exhaustive: never = shape;
🌍

Real-World Examples

  • API state: type FetchState = { status: 'loading' } | { status: 'ok'; data: User } | { status: 'error'; message: string };
  • Form value: type Value = string | number | boolean;
  • React children: type Children = React.ReactNode; (already a union)
  • Redux actions: discriminated unions by type field
💡

Key Facts

  • Discriminated unions with a type or kind field are the most powerful TypeScript pattern
  • TypeScript's control flow analysis tracks narrowing through if/switch/early return
  • never type means 'this should never happen' — use in exhaustive switches
  • Union types of literals ('a' | 'b') are TypeScript's equivalent of enums for many use cases
⚠️

Watch Out!

If you use a union type but forget to narrow before accessing type-specific methods, TypeScript errors. Don't cast with as string to silence the error — use a proper type guard (typeof or instanceof) to prove what type it actually is.

📌

Remember

Union: A | B. Narrow before using type-specific members. Discriminated unions (shared kind field) are the cleanest pattern for state machines.

What You Learned

  • Union types: A | B — value can be either type; narrow before using specific APIs
  • Discriminated unions: shared kind field enables safe pattern matching
  • Unlocks: typed state machines, API states, flexible function parameters

Key Facts

  • Discriminated unions with a type or kind field are the most powerful TypeScript pattern
  • TypeScript's control flow analysis tracks narrowing through if/switch/early return
  • never type means 'this should never happen' — use in exhaustive switches
  • Union types of literals ('a' | 'b') are TypeScript's equivalent of enums for many use cases

Real-World Examples

• API state: <code>type FetchState = { status: 'loading' } | { status: 'ok'; data: User } | { status: 'error'; message: string };</code> • Form value: <code>type Value = string | number | boolean;</code> • React children: <code>type Children = React.ReactNode;</code> (already a union) • Redux actions: discriminated unions by <code>type</code> field

Remember

Union: A | B. Narrow before using type-specific members. Discriminated unions (shared kind field) are the cleanest pattern for state machines.

Quick Quiz

1 / 2

What does string | number mean?