A union type, written with |, describes a value that could be any one of several types — TypeScript only allows operations valid on every member of the union unless the type has been narrowed first. An intersection type, written with &, combines multiple types into one that has all of their members simultaneously, commonly used to merge two separate object type shapes into a single richer one.
Union types: one of several possibilities
Without narrowing, TypeScript only permits operations valid on EVERY member of the union — calling .toUpperCase() directly would be a compile error, since a number doesn't have that method.
function printId(id: string | number) {
// id.toUpperCase(); // COMPILE ERROR: number doesn't have toUpperCase
if (typeof id === "string") {
console.log(id.toUpperCase()); // safe: TypeScript knows id is a string here
} else {
console.log(id);
}
}Union of literal types
A union isn't limited to whole types like string or number — unioning specific literal values creates a precise, closed set of allowed values, similar in spirit to an enum but often more idiomatic in TypeScript.
type Direction = "north" | "south" | "east" | "west";
function move(direction: Direction) {
console.log(`Moving ${direction}`);
}
move("north"); // fine
// move("up"); // COMPILE ERROR: "up" is not assignable to type DirectionIntersection types: combining shapes into one
The resulting type must satisfy both original types at once — an intersection of two object types has every property from both.
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge; // must have BOTH name and age
const p: Person = { name: "Alice", age: 30 }; // must satisfy both types
// const bad: Person = { name: "Bob" }; // COMPILE ERROR: missing 'age'