HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptCritical

Transform union type to intersection type

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
intersectiontypetransformunion

Problem

Is there a way to transform a union type into an intersection type :

type FunctionUnion = (() => void) | ((p: string) => void)
type FunctionIntersection = (() => void) & ((p: string) => void)


I would like to apply a transformation to FunctionUnion to get FunctionIntersection

Solution

You want union to intersection? Distributive conditional types and inference from conditional types can do that. (Don't think it's possible to do intersection-to-union though, sorry) Here's the evil magic:

type UnionToIntersection = 
  (U extends any ? (x: U)=>void : never) extends ((x: infer I)=>void) ? I : never


That distributes the union U and repackages it into a new union where all the consitutents are in contravariant position. That allows the type to be inferred as an intersection I, as mentioned in the handbook:

Likewise, multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.

Let's see if it works.

First let me parenthesize your FunctionUnion and FunctionIntersection because TypeScript seems to bind the union/intersection more tightly than function return:

type FunctionUnion = (() => void) | ((p: string) => void);
type FunctionIntersection = (() => void) & ((p: string) => void);


Testing:

type SynthesizedFunctionIntersection = UnionToIntersection
// inspects as 
// type SynthesizedFunctionIntersection = (() => void) & ((p: string) => void)


Looks good!

Be careful that in general UnionToIntersection<> exposes some details of what TypeScript thinks is an actual union. For example, boolean is apparently internally represented as true | false, so

type Weird = UnionToIntersection


becomes

type Weird = string & number & true & false


which in TS3.6+ gets eagerly reduced to

type Weird = never


because it's impossible to have a value which is string and number and true and false.

Code Snippets

type UnionToIntersection<U> = 
  (U extends any ? (x: U)=>void : never) extends ((x: infer I)=>void) ? I : never
type FunctionUnion = (() => void) | ((p: string) => void);
type FunctionIntersection = (() => void) & ((p: string) => void);
type SynthesizedFunctionIntersection = UnionToIntersection<FunctionUnion>
// inspects as 
// type SynthesizedFunctionIntersection = (() => void) & ((p: string) => void)
type Weird = UnionToIntersection<string | number | boolean>
type Weird = string & number & true & false

Context

Stack Overflow Q#50374908, score: 481

Revisions (0)

No revisions yet.