typescript

UnionToIntersection

type UnionToIntersection<U> = (U extends any ? (_: U) => void : never) extends (_: infer I) => void ? I : never;

An easy way to convert a union to an intersection. It uses the fact that a union of functions must be called with the intersection of all parameter types. Here, it is distributing U into a union of function types, and then inferring the resulting argument. The result of the inference is the intersection of all members in the union.

As an example, let’s say we want to turn this union into an intersection:

type FooBar = UnionToIntersection<{ foo: string } | { bar: number }>;

Distributing this union into function types gives us

((_: { foo: string }) => void) | ((_: { bar: number }) => void)

Then inferring the first argument of this type gives us

{ foo: string } & { bar: number }

But why is it an intersection? Let’s say we actually had this union of functions in our code:

declare const fn: ((_: { foo: string }) => void) | ((_: { bar: number }) => void);

Well, how would we safely call it? You can’t call it with just foo or just bar

fn({ foo: "str" }); // unsafe, since `fn` may be `(_: { bar: number }) => void`

fn({ bar: 12345 }); // unsafe, since `fn` may be `(_: { foo: string }) => void`

Now it should be apparent that the only way to safely call this function is to somehow satisfy both { foo: string } and { bar: number }. Since we need an “and”, we use an intersection. Therefore, to safely call this function, we need to pass a value that is assignable to { foo: string } & { bar: number }.