This is one of the most important comparative topics in TypeScript: Unions vs Intersections.
Let’s create a full, unique article that contrasts them side by side with real-world examples from React, Angular, Vue, and Node.js.
TypeScript provides two essential type operators that often confuse developers:
- Union (
|) – either one type or another. - Intersection (
&) – must satisfy all types at once.
Understanding when to use each will make your codebase more robust, readable, and type-safe.
1. Conceptual Difference
Think of sets in mathematics:
- Union (
|) = all elements from A and B. - Intersection (
&) = only elements that belong to both A and B simultaneously.
2. Union Types (|)
A union allows a value to be one of several possible types.
type Id = string | number;
let userId: Id;
userId = "abc123"; // ✅ string
userId = 42; // ✅ number
userId = true; // ❌ not allowed
👉 Great when you have alternative possibilities.
3. Intersection Types (&)
An intersection combines multiple types into one.
The resulting type must satisfy all constraints.
type HasId = { id: number };
type HasName = { name: string };
type User = HasId & HasName;
const u: User = { id: 1, name: "Alice" }; // ✅ must have both
👉 Great when you want composition of features.
4. Comparison Table
| Feature | Union (|) | Intersection (&) |
|———|————-|———————|
| Meaning | Either type A or B | Must be type A and B |
| Flexibility | More flexible (choice) | More strict (combination) |
| Objects | Value can match any one shape | Value must include all properties |
| Functions | Accepts broader inputs | Requires multiple call signatures |
| Use Case | APIs returning multiple formats | Composing reusable models |
5. Real-World Examples
(a) React Props
Union – Flexible props (only one required)
type ButtonProps =
| { variant: "text"; onClick: () => void }
| { variant: "link"; href: string };
const Button = (props: ButtonProps) => {
if (props.variant === "text") {
return <button onClick={props.onClick}>Click me</button>;
}
return <a href={props.href}>Go</a>;
};
👉 User can pass either onClick or href.
Intersection – Extending props
type BaseProps = { disabled?: boolean };
type ButtonProps = BaseProps & { label: string; onClick: () => void };
const Button = ({ label, disabled, onClick }: ButtonProps) => (
<button disabled={disabled} onClick={onClick}>{label}</button>
);
👉 User must provide all required fields.
(b) Angular Models
Union – API response (success or error)
type ApiSuccess = { status: "success"; data: any };
type ApiError = { status: "error"; message: string };
type ApiResponse = ApiSuccess | ApiError;
function handleResponse(res: ApiResponse) {
if (res.status === "success") {
console.log(res.data);
} else {
console.error(res.message);
}
}
Intersection – Entity + Timestamps
type Entity = { id: string };
type Timestamps = { createdAt: Date; updatedAt: Date };
type Product = Entity & Timestamps & { name: string };
(c) Vue with Composition API
Union – Prop with multiple allowed values
type Size = "small" | "medium" | "large";
export default defineComponent({
props: {
size: { type: String as PropType<Size>, required: true }
}
});
Intersection – Combine mixins
type WithLoading = { loading: boolean };
type WithError = { error?: string };
type ComponentState = WithLoading & WithError;
(d) Node.js API Models
Union – Different request body shapes
type LoginBody = { email: string; password: string };
type RegisterBody = { name: string; email: string; password: string };
type AuthRequest = LoginBody | RegisterBody;
Intersection – Enriching database entities
type DbEntity = { id: string };
type User = { name: string; email: string };
type Auditable = { createdAt: Date; updatedAt: Date };
type UserEntity = DbEntity & User & Auditable;
6. Pitfalls
Union pitfalls
- Too wide → TypeScript cannot narrow automatically.
- Need type guards (e.g.,
if ("prop" in obj)).
Intersection pitfalls
- Conflicting properties = type becomes never.
type A = { x: string }; type B = { x: number }; type C = A & B; // ❌ x impossible → never
7. Best Practices
- ✅ Use union for alternatives (API responses, variant props).
- ✅ Use intersection for composition (adding features, combining entities).
- ✅ Combine them for maximum expressiveness:
type Admin = { role: "admin" } & (User | Moderator); - ❌ Avoid deeply nested unions/intersections → hurts readability.
8. Conclusion
- Union (
|) = choice (one of many). - Intersection (
&) = combination (all at once). - Both are foundations of TypeScript’s type system.
- Mastering them is key for writing robust React, Angular, Vue, and Node.js applications.