TypeScript中有许多的内置类型,比如Partial
、Pick
、Record
、Exclude
等等,还有许多关键字infer
、extends
等等,正因为有了它们的存在,我们才可以随心所欲的组合获取到我们想要的类型。
相信许多同学刚接触的TS的时候对infer
的用法都不是很了解,包括笔者也是,后面随着慢慢的积累,渐渐对它的用法有了自己的理解,这里通过简单的例子帮助不熟悉的同学了解,也自己做个记录方便复盘。
首先,我们模拟一个场景。用过Ant Design Pro的同学,翻看官网的时候肯定看到过文档里面会有一个OpenAPI的介绍,通过这个插件可以很方便的从Swagger的JSON文件转成我们需要的前端请求代码,还附带了各种注释和类型注解(前提是后端同学也有Swagger注释),我们调用的时候就可以直接敲这个API的几个字符,直接出来对应的引入和完整的函数名,简直不要太方便。但是有个问题也暴露出来,当我们需要某些类型的时候,怎么方便的获取出来呢?
某个获取用户信息的接口定义如下:
1 | type Item = { |
接下来我们想获取这个 Item
的类型。如果是用OpenAPI去生成的这段代码,这个类型是没有export
出来的,我们想直接用它的话,还得去改生成后的代码,但是这是极不推荐的,或者去改OpenAPI这个生成器本身代码,但是这个复杂度就很高了。那么有没有一个简单通用的办法,通过定义个类似于Exclude
这种类型函数的东西,直接传个类型进去,直接返回Item
类型呢?当然有。
先把类型函数的架子搭好
1 | type GetDataType<T> = {} |
然后我们需要限制一下传入的泛型类型,这里通过extends
关键字来限制,大概意思就是传入的泛型,必须是继承了(...args: any) => any
这个类型的类型。
1 | type GetDataType<T extends (...args: any) => any> = {} |
然后,传入的泛型是个函数类型,我们需要获取它的返回值类型,TS有内置一个叫ReturnType
的类型,可以传入函数类型之类,得到它的返回类型,正是我们需要的。
1 | type GetDataType<T extends (...args: any) => any> = ReturnType<T> |
这时候,我们已经能获取到返回的类型Promise<Item[]>
了。
接下来,如何把Promise<Item[]>
里面传入的泛型抽离出来呢?这时候就需要我们的主角上场了——infer
.
首先,普及一个知识点,在TS类型中等号右边的extends
关键字,就不再是单纯的继承的意思了,更多的时候是用于三目判断表达式,类似于JS中的三目运算符。
比如
1 | type B = string extends number ? 'string' : 'number' |
上述类型表达式的意思大概是,string
是不是继承于number
,是的话,就返回'string'
类型,不是的话就返回'number'
类型。
其次,infer
的大概含义,我们也需要做一个解释,字面上infer
的意思就是推断的意思,但是实际上,我们可以把它看做一个占位符。
1 | type GetDataType<T extends (...args: any) => any> = ReturnType<T> extends Promise<infer E> |
通过上述代码,我们就可以很轻松的获取到Promise<Item[]>
中的Item[]
类型了。
简单解释一下上述代码。ReturnType<T>
是已经获取到返回的Promise<Item[]>
类型,然后通过extends
关键字去判断,ReturnType<T>
是不是继承于Promise<infer E>
?是的话,就返回占位符E
,不是的话,就返回never
。
同样的原理,我们还可以进一步获取到Item
的类型。
1 | type GetDataType<T extends (...args: any) => any> = ReturnType<T> extends Promise<infer E> |
就这样,我们成功的用extends
和infer
成功提取到了Item
这个类型,如果还有类似的接口需要获取到类似的类型,我们还可以这么干,比如
1 | type Data = { |