C.W.K.
Stream
Lesson 02 of 05 · published

Intersection Types: `A & B`

~9 min · unions-intersections, intersection, composition

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"An intersection is a type that has every property from every part. It's how you compose shapes."

What intersections describe

An intersection type A & B describes a value that has all the members of A AND all the members of B. For object types, this is composition: the result has every property of both parts, with conflicting properties having to be compatible.

For primitives, intersection rarely makes sense — string & number has no values (no value is both a string and a number), so it becomes never. For object shapes, intersection is the workhorse: combining shared base types with role-specific extensions.

Composition patterns

The classic shape: type Admin = User & { permissions: string[] }. The result is User plus the permissions field. Equivalent to interface Admin extends User { permissions: string[] }, but works as a type alias and composes more flexibly.

Multiple intersections are common: type AuditedAdmin = User & Timestamped & Admin. The order doesn't matter — intersection is associative and commutative. The result has every property from every contributing type.

Branded types — the famous use case

Intersection is also how branded types work: type UserId = number & { __brand: 'UserId' }. The intersection of number with a unique tag makes a nominally-distinct type that's still a number at runtime. This pattern shows up in the structural-typing lesson — and it's intersection that makes it possible.

The slogan: union is OR, intersection is AND. Use union when a value is one of several types. Use intersection when a value is several types combined into one shape.

Pippa's confession

cwkPippa uses intersection less than union — most data shapes are 'this or that,' not 'this plus that.' But where intersection earns its keep is in branded IDs and in cross-cutting traits like Timestamped that any shape can opt into. The asymmetry is right: union for variability, intersection for composition.

Code

Intersection — composition and branding·typescript
// Intersection — combining shapes.
type User = { id: number; name: string };
type Timestamped = { createdAt: Date; updatedAt: Date };
type Audited = { auditTrail: string[] };

type AuditedUser = User & Timestamped & Audited;
// AuditedUser has: id, name, createdAt, updatedAt, auditTrail

const u: AuditedUser = {
  id: 1, name: 'Pippa',
  createdAt: new Date(), updatedAt: new Date(),
  auditTrail: [],
};

// Branded types via intersection.
type UserId = number & { __brand: 'UserId' };
const id = 1 as UserId;
const raw: number = id;       // ✅ widens to number
const back: UserId = raw;     // ❌ raw number not assignable to UserId

External links

Exercise

Define Identifiable = { id: number }, Named = { name: string }, Timestamped = { createdAt: Date }. Then declare type Entity = Identifiable & Named & Timestamped. Create one. Now try to write a User interface that extends Identifiable and Named — does it equal Entity minus Timestamped?
Hint
Both intersection and interface-extends produce the same composition. Intersection scales better when you mix concrete types; interface extends scales better when the parts are themselves named interfaces. The choice is style.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.