type-challenges / type-challenges

Collection of TypeScript type challenges with online judge
https://tsch.js.org/
MIT License
42.98k stars 4.67k forks source link

55 - Union to Intersection #1051

Open mistlog opened 3 years ago

mistlog commented 3 years ago

use typetype:

type function toFunctionUnion = (union) => ^{
    if(union extends infer ItemType) {
        return type (arg: ItemType) => any
    } else {
        return never
    }
}

export type function UnionToIntersection = (union) => ^{
    if(toFunctionUnion<union> extends type (arg: infer ArgType) => any) {
        return ArgType
    } else {
        return never
    }
}
mistlog commented 3 years ago

How to create intersection type?

ref: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types

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

type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
  ? U
  : never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number

the clue is that we have to create function types and get intersection type from params.

How to relate this example to our problem?

we can only create union of function types from union, so the basic demo related to our problem is:

type Bar<T> = T extends (x: infer U) => void | ((x: infer U) => void )
    ? U
    : never;
type T20 = Bar<(x: string) => void | ((x: string) => void)>; // string
type T21 = Bar<(x: string) => void | ((x: number) => void)>; // string & number = never

to make it clear:

type Bar<T> = T extends (x: infer U) => void | ((x: infer U) => void ) ? U : never;
type T20 = Bar<(x: {a: 1}) => void | ((x: {b:1}) => void)>; // { a: 1; } & { b: 1; }

Solution

step 1: create union of functions:

type function toFunctionUnion = (union) => ^{
    if(union extends infer ItemType) {
        return type (arg: ItemType) => any
    } else {
        return never
    }
}

step 2: get intersection type from function params

export type function UnionToIntersection = (union) => ^{
    if(toFunctionUnion<union> extends type (arg: infer ArgType) => any) {
        return ArgType
    } else {
        return never
    }
}