巧用 TypeScript(五)---- infer
介绍
infer
最早出现在此 PR 中,表示在 extends
条件语句中待推断的类型变量。
简单示例如下:
type ParamType = T extends (param: infer P) => any ? P : T;
在这个条件语句
T extends (param: infer P) => any ? P : T
中,infer P
表示待推断的函数参数。整句表示为:如果
T
能赋值给 (param: infer P) => any
,则结果是 (param: infer P) => any
类型中的参数 P
,否则返回为 T
。interface User {
name: string;
age: number;
}type Func = (user: User) => voidtype Param = ParamType;
// Param = User
type AA = ParamType;
// string
内置类型 在 2.8 版本中,TypeScript 内置了一些与
infer
有关的映射类型:- 用于提取函数类型的返回值类型:
type ReturnType
= T extends (...args: any[]) => infer P ? P : any;
相比于文章开始给出的示例,ReturnType
只是将infer P
从参数位置移动到返回值位置,因此此时P
即是表示待推断的返回值类型。
type Func = () => User; type Test = ReturnType
; // Test = User
- 用于提取构造函数中参数(实例)类型:
一个构造函数可以使用new
来实例化,因此它的类型通常表示如下:
type Constructor = new (...args: any[]) => any;
当infer
用于构造函数类型中,可用于参数位置new (...args: infer P) => any;
和返回值位置new (...args: any[]) => infer P;
。
因此就内置如下两个映射类型:
// 获取参数类型 type ConstructorParameters
any> = T extends new (...args: infer P) => any ? P : never; // 获取实例类型 type InstanceType any> = T extends new (...args: any[]) => infer R ? R : any; class TestClass {constructor( public name: string, public string: number ) {} }type Params = ConstructorParameters ; // [string, numbder]type Instance = InstanceType ; // TestClass
infer
已有基本了解,我们来看看一些使用它的「骚操作」:- tuple 转 union ,如:
[string, number]
->string | number
【巧用 TypeScript(五)---- infer】解答之前,我们需要了解 tuple 类型在一定条件下,是可以赋值给数组类型:
type TTuple = [string, number]; type TArray = Array; type Res = TTuple extends TArray ? true : false; // true type ResO = TArray extends TTuple ? true : false; // false
因此,在配合infer
时,这很容做到:
type ElementOf
= T extends Array ? E : nevertype TTuple = [string, number]; type ToUnion = ElementOf; // string | number
在 stackoverflow 上看到另一种解法,比较简(牛)单(逼):
type TTuple = [string, number]; type Res = TTuple[number]; // string | number
- union 转 intersection,如:
string | number
->string & number
这个可能要稍微麻烦一点,需要infer
配合「 Distributive conditional types 」使用。
在相关链接中,我们可以了解到「Distributive conditional types」是由「naked type parameter」构成的条件类型。而「naked type parameter」表示没有被Wrapped
的类型(如:Array
、[T]
、Promise
等都是不是「naked type parameter」)。「Distributive conditional types」主要用于拆分extends
左边部分的联合类型,举个例子:在条件类型T extends U ? X : Y
中,当T
是A | B
时,会拆分成A extends U ? X : Y | B extends U ? X : Y
;
有了这个前提,再利用在逆变位置上,同一类型变量的多个候选类型将会被推断为交叉类型的特性,即
type Bar
= 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
因此,综合以上几点,我们可以得到在 stackoverflow 上的一个答案:
type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; type Result = UnionToIntersection; // string & number
当传入string | number
时:
- 第一步:
(U extends any ? (k: U) => void : never)
会把 union 拆分成(string extends any ? (k: string) => void : never) | (number extends any ? (k: number)=> void : never)
,即是得到(k: string) => void | (k: number) => void
; - 第二步:
(k: string) => void | (k: number) => void extends ((k: infer I)) => void ? I : never
,根据上文,可以推断出I
为string & number
。
- 第一步:
LeetCode 的一道 TypeScript 面试题 前段时间,在 GitHub 上,发现一道来自 LeetCode TypeScript 的面试题,比较有意思,题目的大致意思是:
假设有一个这样的类型(原题中给出的是类,这里简化为 interface):
interface Module {
count: number;
message: string;
asyncMethod(input: Promise): Promise>;
syncMethod(action: Action): Action;
}
在经过
Connect
函数之后,返回值类型为type Result {
asyncMethod(input: T): Action;
syncMethod(action: T): Action;
}
其中
Action
的定义为:interface Action {
payload?: T
type: string
}
这里主要考察两点
- 挑选出函数
- 此篇文章所提及的
infer
type FuncName= {
[P in keyof T]: T[P] extends Function ? P : never;
}[keyof T];
type Connect = (module: Module) => { [T in FuncName]: Module[T] }
/*
* type Connect = (module: Module) => {
*asyncMethod: (input: Promise) => Promise>;
*syncMethod: (action: Action) => Action;
* }
*/
接下来就比较简单了,主要是利用条件类型 +
infer
,如果函数可以赋值给 asyncMethod(input: Promise): Promise>
,则取值为 asyncMethod(input: T): Action
。具体答案就不给出了,感兴趣的小伙伴可以尝试一下。更多
- 深入理解 TypeScript
- 巧用 TypeScript(四)
- 巧用 TypeScript(三)
- 巧用 TypeScript(二)
- 巧用 TypeScript(一)
- 从 unit 得到 tuple
- TypeScript 2.8
- unit to intersection
文章图片
推荐阅读
- android第三方框架(五)ButterKnife
- 野营记-第五章|野营记-第五章 讨伐梦魇兽
- 【故障公告】周五下午的一次突发故障
- 华为旁!大社区、地铁新盘,佳兆业城市广场五期!
- 五年后,我要成为独立自强自信的女性
- 二十年后的家乡
- 《格列佛游记》第二卷第五章概括
- 六月更新的......
- 2021-05-05五一的五天假期结束了
- 阿菘的ScalersTalk第五轮新概念朗读持续力训练Day15|阿菘的ScalersTalk第五轮新概念朗读持续力训练Day15 20191025