ShaharAmir
← Back to Blog
TypeScript2 min read

TypeScript Discriminated Unions

Type-safe state handling with a shared property that narrows the type

S
Shahar Amir

The Pattern

A discriminated union uses a common property to differentiate types.

typescript
1234
type Result<T> =
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };

Why It's Powerful

TypeScript narrows the type automatically:

typescript
123456789101112131415
function handleResult(result: Result<User>) {
switch (result.status) {
case 'loading':
// TypeScript knows: no data, no error
return <Spinner />;
case 'success':
// TypeScript knows: result.data exists
return <UserCard user={result.data} />;
case 'error':
// TypeScript knows: result.error exists
return <ErrorMsg error={result.error} />;
}
}

Real World: API Response

typescript
1234567891011121314151617181920
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
async function fetchUser(id: string): Promise<ApiResponse<User>> {
try {
const user = await api.getUser(id);
return { ok: true, data: user };
} catch (e) {
return { ok: false, error: e.message };
}
}
// Usage - TypeScript enforces the check
const result = await fetchUser('123');
if (result.ok) {
console.log(result.data.name); // ✅ Safe
} else {
console.log(result.error); // ✅ Safe
}

Real World: Form State

typescript
123456789101112131415
type FormState =
| { step: 'info'; name: string; email: string }
| { step: 'payment'; cardNumber: string }
| { step: 'confirm'; orderId: string };
function FormStep({ state }: { state: FormState }) {
switch (state.step) {
case 'info':
return <InfoForm name={state.name} />;
case 'payment':
return <PaymentForm card={state.cardNumber} />;
case 'confirm':
return <Confirmation id={state.orderId} />;
}
}

The Discriminator

The shared property (status, ok, step) is called the discriminator.

Pick descriptive names. Your future self will thank you.

#unions#types#patterns#type-safety

Stay Updated 📬

Get the latest tips and tutorials delivered to your inbox. No spam, unsubscribe anytime.