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