ts
    Conditional types
    常用于类型分发

    1. T extends U ? T : never

    原理类似三目,意思就是 T 是否是 U 的子类型,是返回 ? 侧的 T,不是返回 : 侧的 never;

    Typescript 本身是(Structural Type System 结构类型系统),意思大致就是只要两个类型定义的 形状结构(Shape)相同,类似鸭子类型的概念,这两个类型就相同。
    https://www.typescriptlang.org/docs/handbook/interfaces.html

    One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

    Conditional types 就是利用这个特性,T 的结构能够与 U 重合(T 可分配给 U),那 T 就是 U 的子类型。

    基本的使用就是取两个联合类型的 交集(Intersection) 或 差集:

    1. /**
    2. * Exclude from T those types that are assignable to U
    3. */
    4. type Exclude<T, U> = T extends U ? never : T;
    5. /**
    6. * Extract from T those types that are assignable to U
    7. */
    8. type Extract<T, U> = T extends U ? T : never;
    9. type A = 'a' | 'b' | 'c' | 'd';
    10. type B = 'a' | 'c' | 'f';
    11. type Intersection = Extract<A, B>; // A & B
    12. type Union = A | B;
    13. type Difference = Exclude<A, B>;

    用于函数重载:
    https://www.typescriptlang.org/docs/handbook/2/conditional-types.html
    number,string或 number | string 都可被分配给 number | string,所以可以使用 Conditional types 进行 T 类型的校验,并分配到具体的子类型。

    1. interface IdLabel {
    2. id: number /* some fields */;
    3. }
    4. interface NameLabel {
    5. name: string /* other fields */;
    6. }
    7. function createLabel(id: number): IdLabel;
    8. function createLabel(name: string): NameLabel;
    9. function createLabel(nameOrId: string | number): IdLabel | NameLabel;
    10. function createLabel(nameOrId: string | number): IdLabel | NameLabel {
    11. // unimplemented;
    12. }
    13. // 可替换为
    14. type NameOrId<T extends number | string> = T extends number
    15. ? IdLabel
    16. : NameLabel;
    17. function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
    18. // unimplemented;
    19. }
    20. let a = createLabel("typescript");
    21. // ^ = let a: NameLabel
    22. let b = createLabel(2.8);
    23. // ^ = let b: IdLabel
    24. let c = createLabel(Math.random() ? "hello" : 42);
    25. // ^ = let c: NameLabel | IdLabel

    同时 配合 infer 可以实现更多效果,如内置的:
    infer 相当于在 类型分发 过程中的占位符。

    1. /**
    2. * Obtain the parameters of a function type in a tuple
    3. */
    4. type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
    5. /**
    6. * Obtain the parameters of a constructor function type in a tuple
    7. */
    8. type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
    9. /**
    10. * Obtain the return type of a function type
    11. */
    12. type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
    13. // 实现一个取{ [key: string]: (a: unknow) => void } 结构体中所有 a 的交叉类型;
    14. type ParamsIntersection<T> = T extends {
    15. [key: string]: (x: infer U) => void;
    16. }
    17. ? U
    18. : never;
    19. type Params = ParamsIntersection<{
    20. a: (a: string) => void;
    21. b: (b: { b: number }) => void;
    22. }>; // string & { b: number; }

    以及可能有点用的 UnionType -> IntersectionType:
    https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type
    原理:
    https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types
    https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types

    深度学习 Conditional Type:
    https://zhuanlan.zhihu.com/p/54193438