TypeScript 条件类型的 infer 类型推断能力

类型推断 infer 是做为 extends 条件类型的子语句使用,同时在 TS2.8 推出。(若是你不熟悉 extends 条件类型能够查看以前分享的 TypeScript 的 extends 条件类型html

书写格式

使用 infer 声明一个类型变量,在 条件类型断定为 true 时生效,例如:typescript

type ExtractSelf<T> = T extends (infer U) ? U : T;

type T1 = ExtractSelf<string>;        // string
type T2 = ExtractSelf<() => void>;    // () => void
type T3 = ExtractSelf<Date[]>;        // Date[]
type T4 = ExtractSelf<{ a: string }>; // { a: string }
复制代码

上面的 infer U 语句就是声明一个类型变量 U(它能够是任意字母或单词),变量 U 会解析 T 类型。数组

这里的解析规则很简单: U 等于 T,而后返回 U。(根据执行优先级,这里能够去掉 infer 语法两边的括号 () ,而有时必须加上,例如:(infer U)[]函数

推断的规则

上面的例子只是方便咱们认识它,实际场景不会这么用,由于没有意义。咱们升级上面的写法,用于取出数组中的类型:post

type ExtractArrayItemType<T> = T extends (infer U)[] ? U : T;

// 条件判断都为 false,返回 T
type T1 = ExtractArrayItemType<string>;         // string
type T2 = ExtractArrayItemType<() => number>;   // () => number
type T4 = ExtractArrayItemType<{ a: string }>;  // { a: string }

// 条件判断为 true,返回 U
type T3 = ExtractArrayItemType<Date[]>;     // Date
复制代码

经过解析 T 的格式,判断 (infer U)[] 可被分配值 Date[] ,所以条件类型为 true 。而后根据变量 U 所在的位置,推断 U 等于 Date。优化

让咱们再修改一下,实现获取函数返回值类型的功能(相似于官方预置的 ReturnType 高级类型):ui

type ExtractReturnType<T> = T extends () => (infer U) ? U : T;

// 条件判断为 true,返回 U
type T1 = ExtractReturnType<() => number>;   // number
复制代码

经过上面两个例子能够看出,infer 声明的类型变量所在的位置,能够匹配出任何想要的值类型。spa

推断出联合类型

假设下面这种状况,同一个类型变量存在于多个位置,且每一个位置上的数据类型不一样,则会推断为 联合类型:code

type ExtractAllType<T> = T extends { x: infer U, y: infer U } ? U : T;

type T1 = ExtractAllType<{ x: string, y: number }>; // string | number
复制代码

这里的 ExtractAllType<T> 中 infer 格式中的属性是固定的 x 和 y,咱们能够优化一下,让它能够接收任意数量:htm

type ExtractAllType<T> = T extends { [k: string]: infer U } ? U : T;

type T1 = ExtractAllType<{ x: string, y: number, z: boolean }>; // string | number | boolean
复制代码

知道这个特性后,咱们再看上面提取数组中的类型的功能,实际上它还能够这么用:

type ExtractArrayItemType<T> = T extends (infer U)[] ? U : T;

type ItemTypes = ExtractArrayItemType<[string, number]>; // string | number
复制代码

这里实现了将 元组类型 转换成 联合类型。

函数重载的规则

重载声明的函数,始终获取最后一个声明,不过须要使用 typeof 功能转换下重置函数的格式:

declare function foo(x: string): number;
declare function foo(x: number): string;
declare function foo(x: string | number): string | number;

type 1 = ReturnType<typeof foo>;  // string | number
复制代码
相关文章
相关标签/搜索