Type intersections


& in TypeScript is called the intersection operator.

Why tho? If you’re familiar with set intersections it might seem like & works exactly the wrong way around… like, intersecting the sets {1, 2, 3} and {2, 3, 4} gives you {2, 3} — it’s the subset of elements that exist in both sets.

But in Typescript, { foo: string } & { bar: number } gives { foo: string, bar: number } — we get all properties of either type. Isn’t that more like a union?

Well

No.

There’s a very formal reason why the “type intersection” is called as such, and it has to do with the Liskov Substitution Principle.

Take a look at the following function definitions:

const foo = (x: { foo: string }) => x.foo;
const bar = (x: { bar: number }) => x.bar;

foo takes Foos, and bar takes Bars, but our intersection type { foo: string } & { bar: number } can be passed to either function.

So if you imagine { foo: string } and { bar: number } as circles on a Venn Diagram, then our intersection type is like the middle part. Our foobar can go to the foo club and the bar club, so to speak, because the foobar is a subtype of the foos and a subtype of the bars.

The Venn Diagram and it’s intersection in this case doesn’t show a subset relationship, but rather a subtype relationship!

It is kind of funny tho how the type with more properties will be a subtype of the type with fewer properties but that’s just what follows from the above.

And what follows from that is that the empty type {} isn’t actually a subtype of any other type, while the empty set {} is a subset of every set. The “empty set” equivalent of types would be the type that contains all properties, and where each property can be anything.

Turns out typescript has not one but two built-in types for that: unknown, and any.