• home > webfront > ECMAS > typescript >

    TS数据类型(2):类型模式匹配技术——条件类型+infer

    Author:zhoulujun Date:

    条件类型允许我们检测两种类型之间的关系,通过条件类型我们就可以判断两种类型是否相兼容。 infer 用于声明类型变量,以存储在模式匹配过程中所捕获的类型。

    类型模式匹配技术

    TypeScript 提供的类型模式匹配技术 —— 条件类型 + infer。

    • 条件类型允许我们检测两种类型之间的关系,通过条件类型我们就可以判断两种类型是否相兼容。

    •  infer 用于声明类型变量,以存储在模式匹配过程中所捕获的类型

    条件类型:T extends U ? X : Y

    当类型 T 可以赋值给类型 U 时,那么返回类型 X,否则返回类型 Y

    2.webp

    那么条件类型有什么用呢?这里我们来举个例子:

    type IsString<T> = T extends string ? true : false;
    type I0 = IsString<number>;  // false
    type I1 = IsString<"abc">;  // true
    type I2 = IsString<any>;  // boolean
    type I3 = IsString<never>;  // never

    4.webp

    在以上代码中,我们定义了 IsString 工具类型。使用该工具类型,我们可以判断传给类型参数 T 的实际类型是否为字符串类型。除了判断单一类型之外,利用条件类型和条件链,我们还可以同时判断多种类型。

    接下来,我们来看一下如何实现该功能:

    type TypeName<T> =
        T extends string ? "string" :
        T extends number ? "number" :
        T extends boolean ? "boolean" :
        T extends undefined ? "undefined" :
        T extends Function ? "function" :
        "object";
    
    type T0 = TypeName<string>;  // "string"
    type T1 = TypeName<"a">;  // "string"
    type T2 = TypeName<true>;  // "boolean"
    type T3 = TypeName<() => void>;  // "function"
    type T4 = TypeName<string[]>;  // "object"

    如果传入的类型是联合类型的话,那么将返回什么结果

    type T10 = TypeName<string | (() => void)>;  // "string" | "function"
    type T11 = TypeName<string | string[] | undefined>;  // "string" | "object" | "undefined"

    4.webp

    为什么 T10 和 T11 类型返回的是联合类型呢?这是因为 TypeName 属于分布式条件类型。在条件类型中,如果被检查的类型是一个 “裸” 类型参数,即没有被数组、元组或 Promise 等包装过,则该条件类型被称为分布式条件类型。

    对于分布式条件类型来说,当传入的被检查类型是联合类型的话,在运算过程中会被分解成多个分支。

    T extends U ? X : Y 
    T => A | B | C 
    A | B | C extends U ? X : Y  => 
    (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)

    关于条件类型,更多查看:《用了 TS 条件类型,同事直呼 YYDS! https://juejin.cn/post/7096265620445986823


    infer:T extends (infer U)[] ? U : T

    type U0 = UnpackedArray<T0>
    
    // T => T0: string[]
    type UnpackedArray<string[]> = string[] extends (infer U)[] ? U : string[] 
    // string[] extends (infer U)[] 模式匹配成功
    // U => string

    infer 只能在条件类型的 extends 子句中使用,同时 infer 声明的类型变量只在条件类型的 true 分支中可用。

    当遇到函数重载的场景,TypeScript 将使用最后一个调用签名进行类型推断:

    declare function foo(x: string): number;
    declare function foo(x: number): string;
    declare function foo(x: string | number): string | number;
    
    type UnpackedFn<T> = T extends (...args: any[]) => infer U ? U : T;
    type U2 = UnpackedFn<typeof foo>;  // string | number

    利用条件类型和 infer,我们还可以推断出对象类型中键的类型。

    type User = {
      id: number;
      name: string;
    }
    
    type PropertyType<T> =  T extends { id: infer U, name: infer R } ? [U, R] : T
    type U3 = PropertyType<User> // [number, string]



    https://jkchao.github.io/typescript-book-chinese/tips/infer.html

    学会 TS infer,写起泛型真香! https://www.51cto.com/article/708480.html




    转载本站文章《TS数据类型(2):类型模式匹配技术——条件类型+infer》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/typescript/2022_0923_8877.html