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 a sort of impossible type that contains all properties,
and where each property can be anything.
In typescript, that type is called never.