Intersection Types
An intersection type (&) combines multiple types into one that must satisfy all of them simultaneously.
🎯 What you'll learn
- What an Intersection Type Is
- The Mixin Pattern
- Intersecting Function Types — Overload-Like Behavior
- Intersecting Incompatible Primitives Produces
never - A Common Trap: Same Property Name, Conflicting Types
1What an Intersection Type Is
An intersection type (&) combines multiple types into one that must satisfy all of them simultaneously.
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;
const p: Person = { name: "Ana", age: 30 }; // must have BOTH name and ageCompare with a union (|, its own topic): a union means "could be any one of these"; an intersection means "must be every one of these at once."
2The Mixin Pattern
Intersections are the standard way to express "an object that combines the capabilities of several independent pieces" — commonly used for mixin-style composition.
type Timestamped = { createdAt: Date };
type Taggable = { tags: string[] };
type Identifiable = { id: number };
type Post = Identifiable & Timestamped & Taggable & {
title: string;
body: string;
};
const post: Post = {
id: 1,
createdAt: new Date(),
tags: ["typescript"],
title: "Hello",
body: "World",
};3Intersecting Function Types — Overload-Like Behavior
type StringFormatter = (value: string) => string;
type NumberFormatter = (value: number) => string;
type Formatter = StringFormatter & NumberFormatter;
const format: Formatter = (value: string | number): string => {
return typeof value === "string" ? value.toUpperCase() : value.toFixed(2);
};A value assigned to an intersection of function types must be callable in a way compatible with every member signature — in practice this behaves similarly to (but is not identical to) function overloads.
4Intersecting Incompatible Primitives Produces never
type Impossible = string & number; // collapses to `never` — no value can be both at onceIf two intersected types have genuinely incompatible requirements for the same slot (like being both string and number simultaneously), the result is never — the type that has no possible values at all (full depth in the Any/Unknown/Never topic).
5A Common Trap: Same Property Name, Conflicting Types
type A = { value: string };
type B = { value: number };
type Combined = A & B; // value: string & number → collapses to `value: never`
// const c: Combined = { value: "hi" }; // ❌ impossible — never accepts no valuesEven though A and B are otherwise perfectly normal object types, intersecting them when they share a property name with incompatible types silently produces an unusable never for that property — not a compile error on the type Combined = A & B line itself, but on any attempt to actually construct a value of that type.
Under the HoodHow Intersection Is Actually Computed▾
Object-type intersections merge member lists, per-property
For object-like types, the checker computes an intersection by walking each source type's member list and merging them into one combined member list, property by property. When both sides define the same property name, the checker doesn't pick one arbitrarily — it computes the intersection of that property's own type too, recursively applying the same rule.
A = { value: string } B = { value: number }
↓ merge property "value"
Combined.value : string & number → never (section 4's rule, applied per-property)This is exactly why section 5's trap happens: the collapse to never isn't a special-cased error condition — it falls straight out of the same general merging algorithm used for every intersection, applied recursively down to the conflicting property.
Why intersections of DIFFERENT, non-overlapping shapes just work
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // no shared property names at all — clean merge, no conflictsSince Named and Aged never define the same property name, the per-property merge in every case has only one candidate type to use — no intersection-of-property-types step is ever triggered, and the result is simply the union of both member lists, which is exactly the mixin pattern from section 2.
Function type intersections use a different rule: overload-like stacking
Object property merging doesn't apply to call signatures the same way — intersecting function types instead makes the resulting type callable via any of the intersected signatures (closer to how overloads work), rather than trying to merge parameter/return types property-by-property the way object shapes do. This is why section 3's example can be called with either a string or a number argument — the checker treats each original signature as still independently valid on the intersected type.
⚠️ Common Pitfalls
- Intersecting two object types that share a property name with different, incompatible types — silently produces an unconstructable
neverfor that property; the error only surfaces later, when you try to actually build a value. - Confusing
&(intersection, "must be all of these") with|(union, "could be any of these") — an easy symbol mix-up with very different meaning. - Expecting intersected function types to behave exactly like formal overloads — close, but not identical; genuinely complex overload requirements are better expressed with real overload signatures (a function/method with multiple declared signatures), not an intersection of function types.
- Assuming intersection order matters for the result — for object shapes,
A & BandB & Aproduce the same combined shape; order doesn't affect the final merged member set.
📋 Quick Reference
A & B → must satisfy BOTH — combines requirements
{x: string} & {x: number} → x collapses to `never`
object shapes, no name clash → clean merge, mixin pattern
function types → callable via either original signature🎤 Interview Q&A
Q1What is an intersection type?core▾
A & B combines types: a value must satisfy both, so it has all properties of A and B. Used to compose types and mixins.
- Union vs intersection? Union: either; intersection: both.
Q2What happens when intersected properties conflict?core▾
Conflicting property types intersect too: { id: string } & { id: number } makes id: never, so no value can satisfy it. Interfaces with extends report such conflicts as errors earlier.
- Why prefer extends sometimes? Clearer errors and better performance.
Q3Where are intersections useful?core▾
Adding properties to existing types (User & { permissions: string[] }), branded types, combining props in React components, and mixin patterns.
- React example?
type Props = ButtonProps & { loading?: boolean }.
Q4How do intersections interact with unions?senior▾
Intersection distributes over unions: (A | B) & C becomes (A & C) | (B & C), useful for adding common fields to each variant of a discriminated union.
- Simplifying display?
type Simplify<T> = { [K in keyof T]: T[K] } & {}.
Q5Intersection of functions?core▾
Behaves like overloads: an intersection of function types can be called with either signature.
- Common use? Rare; usually explicit overloads are clearer.
🛠️ Practice exercises
Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.