今天看了下 React 的类型定义,也就是 @types/react 包下的 index.d.ts,发现了一些有趣的写法。
这篇文章就分享下这些写法,估计大部分人都不知道:
首先,我看到了这样一段类型逻辑:
这段逻辑就是取索引类型的 ref 索引的值,但是是通过模式匹配的方式,把提取的类型放到 infer 声明的局部变量 R 里返回的。
简化一下就是这样的:
提取 Props 的 ref 索引的值的类型返回。
我在想,这么麻烦干什么,直接 Props['ref'] 不就能拿到 ref 索引的值么?
于是我就改成了这样:
然后试了下:
不对呀,人家这是可选索引,值的类型是包含 undefined 的联合类型。
那就 Exclude 下不就行了:
这样也比那个 infer 的方式简洁呀,为啥 React 类型定义都是用的 infer 取的可选索引的类型呢?
后来我突然想到,如果这个 ref 值的类型就是 undefined 呢?
我试了下:
确实,我那样写是有问题的,如果值的类型本来就是 undefined,Exclude 掉 undefined 后就是 never 了,而人家那种方式就没问题:
于是我就加一下 undefined 的处理:
这样就行了。
对比了下两种写法:
确实还是 React 的那种写法更简洁。
对了,那上面那层判断呢?
这个判断没必要的吧,如果没有 ref,那 Props['ref'] 不就是返回 never 么,没必要单独判断呀?
然后我就看到了这样一段注释:
在 ts 3.0 中,如果索引类型没有对应的索引,那返回的类型是 {} 而不是 never。
原来如此,这个 'ref' extends keyof Props 是为了做兼容的呀,不是没意义。
这就是我从这个类型中学到的两个知识点:
我们从这个类型里学到了不少东西,再来看下第二个类型:
然后我又看到了这样一个类型,
先试一下它的功能,传入两个索引类型:
看下结果:
这是些啥啊,谁能看得懂呀。
其实这只是因为 TS 没有计算出最终的类型而已,用到的时候才会计算,所以我们可以这样处理下:
Copy 的高级类型是通过映射类型的语法构造了一个新的索引类型,它的功能是原封不动的复制一个索引类型。
类型参数 Obj 约束为索引类型,也就是 Record。key 保持不变,也就是 Key in keyof Obj,value 也保持不变,也就是 Obj[Key]。
因为重新生成的类型的过程中要做计算,所以那个类型就能提示出最终的结果了:
所以说,这个类型的作用是两个索引类型 A,B,只有 A 中有的就保留,A、B 都有的变为可选,B 有但 A 没有的变为可选。
那这段逻辑具体是怎么用 TS 实现的呢?
我们先来过一下 TS 这些内置的高级类型:
Pick 的作用是通过映射类型的语法构造一个新的索引类型,根据传入的 Key 对索引做下过滤:
type Pick= { [P in K]: T[P]; };
测试下:
Partial 也是通过映射类型的语法构造一个新的索引类型,但是会把索引变为可选:
type Partial= { [P in keyof T]?: T[P]; };
测试下:
Extract 是取两个联合类型都包含的部分,也就是取交集:
type Extract = T extends U ? T : never;
测试下:
Exclude 是从联合类型 A 中去掉联合类型 B 中的类型,也就是取差集:
type Extract = T extends U ? T : never;
测试下:
学会了用 Pick、Partial、Exclude、Extract 这些高级类型,那上面的那段逻辑我们就知道怎么实现了:
只有 A 中有的就保留的逻辑是:Pick>。
A、B 都有的变为可选:Partial>>。
B 中有但 A 中没有的也变为可选:Partial>>。
这样,这个类型的主要逻辑我们就理清了:
把三部分计算结果的索引类型取交叉类型,就会合并到一起。
那前面那两个判断是啥?
P extends any 还有这个 string extends keyof P,这俩都是做啥的?
P extends any 这个是因为联合类型当作为类型参数出现在条件类型左边时,会把每个类型单独传入做计算,最后把计算结果合并成联合类型,这个特性叫做分布式条件类型。
比如这样一个联合类型:
type Union = 'a' | 'b' | 'c';
我们想把其中的 a 大写,就可以这样写:
type UppercaseA- =
Item extends 'a' ? Uppercase- : Item;
因为 Item 是类型参数,出现在了条件类型的左边,而且传入的是联合类型,那就触发分发特性,会把每个类型单独传入做计算,然后把结果合并成联合类型。
所以这里的 P extends any 的作用就是触发联合类型特性的,从而让这个类型能正确处理联合类型。不然联合类型整个传入的话,后面怎么做计算。
这里的 P extends any 换成 P extends P 也可以,都是一样的作用。
那后面那段代码 string extends keyof P 是啥意思?
这个我确实想了一段时间,如果 { a: 1, b: 2} 这样的索引类型,keyof 的结果是 'a' | 'b',而如果是数组类型,那 keyof 的结果是 0 | 1 | 'length' | 'toString' | ...
什么类型的 keyof 结果是 string 呢?
突然,我想起了前几天学到的一个知识点:用 keyof any 代替 string | number | symbol 更灵活:
而且我试了下 never 的 keyof 结果也是这个:
所以说 string extends keyof P 就可以排除 any 和 never 的情况!
妙呀,还能这么区分 any 和 never。
所以说,这个类型的逻辑我们已经理清了:
这个类型的功能是保留只有 A 有的索引,把 A、B 都有的索引变为可选,把只有 B 有的索引变为可选。
而且处理了联合类型的情况。
如果传入的是 any 或者 never,不做处理,直接返回。
这个类型里我们也学到了不少东西。
我看了下 @types/react 的类型定义,学到了不少东西:
而且,还讲了一个小技巧:
ts 类型只有计算的时候才会求值,如果是索引类型,可以用映射类型的语法创建个一摸一样的索引类型,因为用到了,就会做计算,从而就可以显示出最终的类型。
不得不说,React 类型定义做的挺完善的,考虑到了各种类型的处理,也考虑到了低版本的兼容,从中还是能学到不少东西的。
当前名称:从React源码的类型定义中,我学到了什么?
分享URL:http://www.shufengxianlan.com/qtweb/news39/460389.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联