Annotated solution
Published August 22, 2026The solution
type UnionToIntersection<U> = ( U extends unknown ? (arg: U) => void : never ) extends (arg: infer I) => void ? I : never
The common wrong answer
type UnionToIntersection<U> = U extends unknown ? U : never
This distributes and reassembles, which is an identity function: unioning the members back together returns the union you started with. Distribution alone can never build an intersection — something has to change the variance.
Line by line
U extends unknown ? (arg: U) => void : neverA distributive conditional whose only job is to wrap each member in a function. `A | B` becomes `((arg: A) => void) | ((arg: B) => void)` — still a union, but now the members differ only in a parameter position.
extends (arg: infer I) => voidAsking a union of functions for one parameter type forces the compiler to find a type every member would accept. Because parameters are contravariant, that type is the *intersection* of the candidates — which is exactly the answer.
UnionToIntersection<'a' | 'b'>The honest consequence: `"a" & "b"` describes a value that is both string literals at once, which nothing can be, so it collapses to `never`. The type is behaving correctly even though the answer looks like a failure.
Takeaway
Variance is a tool, not just a rule to obey. Inference from a covariant position gives you a union; from a contravariant one it gives you an intersection — and that asymmetry is the only way to cross between them.

