Transform union type to intersection type

后端 未结 2 1809
感动是毒
感动是毒 2020-11-21 14:55

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

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


        
2条回答
  •  轮回少年
    2020-11-21 15:27

    There is also a very related problem when you would like an intersection of several types, but not necessarily convert unions to intersections. There is just no way to get right to intersections without resorting to temporary unions!

    The problem is that types we would like to get an intersection of might have unions inside, which will be converted to intersections too. Guards to the rescue:

    // union to intersection converter by @jcalz
    // Intersect<{ a: 1 } | { b: 2 }> = { a: 1 } & { b: 2 }
    type Intersect = (T extends any ? ((x: T) => 0) : never) extends ((x: infer R) => 0) ? R : never
    
    // get keys of tuple
    // TupleKeys<[string, string, string]> = 0 | 1 | 2
    type TupleKeys = Exclude
    
    // apply { foo: ... } to every type in tuple
    // Foo<[1, 2]> = { 0: { foo: 1 }, 1: { foo: 2 } }
    type Foo = {
        [K in TupleKeys]: {foo: T[K]}
    }
    
    // get union of field types of an object (another answer by @jcalz again, I guess)
    // Values<{ a: string, b: number }> = string | number
    type Values = T[keyof T]
    
    // TS won't believe the result will always have a field "foo"
    // so we have to check for it with a conditional first
    type Unfoo = T extends { foo: any } ? T["foo"] : never
    
    // combine three helpers to get an intersection of all the item types
    type IntersectItems = Unfoo>>>
    
    type Test = [
        { a: 1 } | { b: 2 },
        { c: 3 },
    ]
    
    // this is what we wanted
    type X = IntersectItems // { a: 1, c: 3 } | { b: 2, c: 3 }
    
    // this is not what we wanted
    type Y = Intersect // { a: 1, b: 2, c: 3 }
    

    The execution in the given example goes like this

    IntersectItems<[{ a: 1 } | { b: 2 }, { c: 3 }]> =
    Unfoo>>> =
    Unfoo>> =
    Unfoo> =
    Unfoo<(({ foo: { a: 1 } | { b: 2 } } | { foo: { c: 3 } }) extends any ? ((x: T) => 0) : never) extends ((x: infer R) => 0) ? R : never> =
    Unfoo<(({ foo: { a: 1 } | { b: 2 } } extends any ? ((x: T) => 0) : never) | ({ foo: { c: 3 } } extends any ? ((x: T) => 0) : never)) extends ((x: infer R) => 0) ? R : never> =
    Unfoo<(((x: { foo: { a: 1 } | { b: 2 } }) => 0) | ((x: { foo: { c: 3 } }) => 0)) extends ((x: infer R) => 0) ? R : never> =
    Unfoo<{ foo: { a: 1 } | { b: 2 } } & { foo: { c: 3 } }> =
    ({ foo: { a: 1 } | { b: 2 } } & { foo: { c: 3 } })["foo"] =
    ({ a: 1 } | { b: 2 }) & { c: 3 } =
    { a: 1 } & { c: 3 } | { b: 2 } & { c: 3 }
    

    Hopefully this also shows some other useful techniques.

提交回复
热议问题