#55Hard

Union to Intersection

Turn A | B | C into A & B & C. There is no built-in operator for this; distributive conditionals and contravariant inference get you there.

There is no operator that turns A | B | C into A & B & C. A variance trick gets you one anyway.

UnionToIntersection<U> converts a union of types into an intersection of those same types. The solution relies on two deep mechanics working together: distributive conditional types and contravariant inference of function parameters. This exact pattern shows up all over real-world type libraries, from merging overloads to combining plugin configurations.

For example

[object Object]

Challenge Instructions: Union to Intersection

Hard

Implement the advanced util type UnionToIntersection<U>

For example

[object Object]

View on GitHub: https://tsch.js.org/55

Change the following code to make the test cases pass (no type check errors).

Loading...

Detailed Explanation

The solution in full:

type UnionToIntersection<U> = (
  U extends unknown ? (arg: U) => void : never
) extends (arg: infer I) => void
  ? I
  : never

It's short, but almost every character is doing something subtle.

Step 1: Wrap each union member in a function

The inner expression U extends unknown ? (arg: U) => void : never looks like a no-op: every type extends unknown, so the condition is always true. It's there for a side effect.

When the type being checked before extends is a bare generic parameter, the conditional type becomes distributive: TypeScript applies it to each union member separately and unions the results. For U = 'foo' | 42 | true, the expression evaluates member by member:

// 'foo'  →  (arg: 'foo') => void
// 42     →  (arg: 42) => void
// true   →  (arg: true) => void

So the whole inner expression becomes:

[object Object]

We haven't gotten rid of the union yet. We've just moved every member into function parameter position, and that relocation is the entire point.

Step 2: Infer the parameter back out, contravariantly

Now the outer check asks: does that union of functions extend (arg: infer I) => void? To answer, TypeScript must find a single type I that works as the parameter for all of the function types at once.

The key rule: function parameters are contravariant. A function that can safely stand in for all three of those functions must accept 'foo' and 42 and true, since any caller might pass any of them. So the inferred parameter has to satisfy every constraint simultaneously, and when TypeScript infers a type variable from multiple contravariant positions, it intersects the candidates instead of unioning them:

[object Object]

That variance flip, union in, intersection out, is what makes the solution work. The same infer in a covariant position (like a return type) would have produced a union.

Why the wrapper step is not optional

If you tried U extends (arg: infer I) => void ? I : never directly, distribution would kick in on the outer conditional and each member would be checked on its own. For this page's running example that's an immediate dead end: none of 'foo', 42 or true is a function type, so every distributed branch takes the never path and the whole result is never. Even when the members are function types, like the test case (() => 'foo') | ((i: 42) => true), you'd infer one I per member and union the results right back together: 42 from the second member, unknown from the parameter-less first, giving unknown rather than any intersection. Doing the distribution first and the inference second, on the already-assembled union of functions (wrapped in parentheses so it's no longer a bare type parameter), lets the inference see all members in a single non-distributive check.

The test cases

Once you understand this pattern, you'll recognize it as a building block: several other hard challenges (like UnionToTuple) start by calling UnionToIntersection under the hood.

This challenge is originally from here.

Share this challenge

Learn the Concepts