Skip to main content
← Gists
typescript Feb 19, 2026

Union to Intersection

Merge a union of object types into a single composite type.

UnionToIntersection collapses A | B | C into A & B & C. I reach for it when merging handler maps, plugin interfaces, or mixin types that are collected as a union but need to be consumed as a single composite object.

Type

union-to-intersection.ts
type UnionToIntersection<U> =
(U extends unknown ? (arg: U) => void : never) extends (arg: infer I) => void
  ? I
  : never

type Handlers =
| { onClick: () => void }
| { onKey: (key: string) => void }

type Combined = UnionToIntersection<Handlers>
// Combined is { onClick: () => void } & { onKey: (key: string) => void }

How it works

  • The distributive conditional (U extends unknown ? ... : never) wraps each union member into a function parameter position: (arg: A) => void | (arg: B) => void.
  • Contravariant inference does the heavy lifting. When TypeScript infers a single type I from (arg: infer I) => void across a union of function types, it must find a type that satisfies all parameter positions simultaneously. That’s the intersection.
  • This is a consequence of how function parameters are contravariant in TypeScript: a function that accepts A & B is assignable to both (arg: A) => void and (arg: B) => void.

Common uses

  • Merging plugin registrations where each plugin declares its own slice of a shared context object.
  • Combining event handler maps from multiple sources into a single interface.
  • Flattening mapped types that produce unions of partial objects.