TypeScript Union & Intersection Types
Union types = “or”. Intersection types = “and”.
Union Types
A value can be one of several types:
let id: string | number;
id = "abc123"; // OK
id = 42; // OK
// id = true; // Error
Remember: You can only access properties common to all types in the union.
Narrowing Unions
Use typeof or instanceof to narrow:
function processId(id: string | number): void {
if (typeof id === "string") {
console.log(id.toUpperCase()); // narrowed to string
} else {
console.log(id.toFixed(2)); // narrowed to number
}
}
Literal Union Types
Restrict to specific values:
type Direction = "north" | "south" | "east" | "west";
function move(dir: Direction): void {
switch (dir) {
case "north": console.log("Up"); break;
case "south": console.log("Down"); break;
}
}
Tip: Use literal unions instead of string constants or simple enums.
Intersection Types
Combine multiple types into one:
type CanFly = { fly(): void };
type CanSwim = { swim(): void };
type Duck = CanFly & CanSwim;
let duck: Duck = {
fly() { console.log("Flying"); },
swim() { console.log("Swimming"); }
};
Remember: The value must satisfy ALL types in the intersection.
Combining Union and Intersection
type Admin = { role: string };
type User = { name: string };
type AdminUser = Admin & User; // both
type SuperUser = AdminUser | User; // either
Type Guards
Custom functions that narrow types:
function isString(value: string | number): value is string {
return typeof value === "string";
}
let value: string | number = "hello";
if (isString(value)) {
console.log(value.toUpperCase()); // TypeScript knows it's string
}
Best Practices
- Use unions for “or” scenarios
- Use intersections for “and” scenarios
- Always narrow unions before accessing type-specific properties
- Keep union/intersection types simple — avoid deep nesting
Common Mistakes
- Accessing union-specific properties without narrowing
- Using intersection for “or” logic (use union instead)
- Forgetting to narrow union types
- Confusing
&(intersection) with|(union)