TypeScript Unions
Combine types with union, intersection, and discriminated unions
Overview
Union types express values that can be one of several types (A | B). Intersection types combine shapes (A & B). Discriminated unions add a shared literal field for safe narrowing in switch statements.
Syntax / Usage
type Status = 'idle' | 'loading' | 'success' | 'error'
type Id = string | number
function printId(id: Id) {
if (typeof id === 'string') {
console.log(id.toUpperCase())
} else {
console.log(id.toFixed(0))
}
}
// Intersection
type Named = { name: string }
type Aged = { age: number }
type Person = Named & Aged
// Discriminated union
type ApiState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; message: string }
Examples
Exhaustive switch:
function render(state: ApiState) {
switch (state.status) {
case 'idle':
return null
case 'loading':
return <Spinner />
case 'success':
return <List items={state.data} />
case 'error':
return <Error msg={state.message} />
default: {
const _exhaustive: never = state
return _exhaustive
}
}
}
Optional vs null union:
type Config = {
apiUrl: string
retry?: number
token: string | null
}
Common Mistakes
- Union of incompatible operations without narrowing first
- Missing
nevercheck in default case when adding new variants - Using
|for objects when&intersection was intended - Overly wide unions that should be split into separate functions
See Also
type-guards basic-types interfaces enums