当前位置: 首页 > 科技观察

我从React源代码中的类型定义中学到了什么?

时间:2023-03-18 21:02:11 科技观察

今天看了下React的类型定义,也就是@types/react包下的index.d.ts,发现了一些有趣的写法。本文将分享这些写法,大部分人可能不知道:提取optionalindex的值首先,我看到了这样一段类型逻辑:这个逻辑是取index类型的refindex的值,但是通过modeMatching方法,将提取出来的类型放入infer声明的局部变量R中并返回。简化它是这样的:提取由Props返回的ref索引的值的类型。我就在想,怎么这么麻烦,难道不能直接通过Props['ref']获取ref索引的值吗?所以我改成这样:然后我试了:不,这是一个可选的索引,值的类型是一个包含undefined的联合类型。然后Exclude是不够的:这比infer方法更简单。为什么React类型定义使用可选的索引类型infer?然后突然想到,如果ref值的类型是undefined呢?我试过了:确实,我这样写有问题。如果value的类型是undefined,Exclude在undefined之后就永远不会了,但是另外一种方式是没问题的:所以我会加上undefined处理:这样就可以了。对比下两种写法:确实React的写法更简洁。对了,上层的判断呢?这个判断是不必要的。如果没有ref,那么Props['ref']就直接返回never,就不用单独判断了?然后看到这样的评论:在ts3.0中,如果索引类型没有对应的索引,返回的类型是{},而不是never。原来这个'ref'extendskeyofProps是为了兼容性,并不是没有意义。这是我从这个类型中学到的两个知识点:索引访问Obj[Key]和infer抽取都可以得到索引类型的一个索引的值,但是在处理可选索引的时候,使用infer更简洁一些,因为前者需要取出类型然后单独处理undefined,而后者在infer的时候顺便处理undefined。在ts3.0中,如果索引类型没有对应的索引,则返回{}而不是never。如果兼容性要求高,可以在keyOfObj中使用'xx'进行兼容性。这个类型我们学到了很多,我们来看第二种:索引类型以及any和never的处理然后又看到了这样一个类型,先试试它的功能,传入两个索引类型:看结果:这是什么,谁看得懂。其实这只是因为TS不计算final类型,使用的时候会计算,所以我们可以这样处理:Copy的高级类型通过映射类型的语法构造一个新的索引类型,其作用是完整复制一个索引类型。类型参数Obj被约束为索引类型,即Record。key不变,即keyofObj中的Key,value也不变,即Obj[Key]。因为在重新生成类型的过程中需要进行计算,该类型可以提示最终结果:所以,该类型的作用是两个索引类型A,B,只保留A中的。B有的变成可选的,B有而A没有的变成可选的。那么这个逻辑是如何使用TS实现的呢?我们来看看TS内置的这些高级类型:PickPick的作用是通过映射类型的语法构造一个新的索引类型,并根据传入的KeyFiltering对索引进行如下操作:typePick={[PinK]:T[P];};测试:PartialPartial同样通过映射类型的语法构造了一个新的索引类型,但是会将索引改为Optional:typePartial={[PinkeyofT]?:T[P];};测试中:ExtractExtract是取两种joint类型中包含的部分,即取交集:typeExtract=TextendsU?T:从不;待测:ExcludeExclude是将jointtypeB中的type从jointtypeA中去掉,即差集:typeExtract=TextendsU?T:从不;测试中:学会了使用Pick、Partial、Exclude、Extract这些高级类型,那么我们就知道如何实现上面的逻辑了:只保留A中的逻辑:Pick>。A和B都变成可选的:Partial>>。在B中但不在A中的内容也成为可选的:部分>>。这样我们就理清了这种类型的主要逻辑:将三部分计算结果的指标类型作为交叉类型,合并到一起。前两个判断是什么?Pextendsany和thisstringextendskeyofP,这两个是做什么的?Pextendsany这是因为当联合类型作为类型参数出现在条件类型的左侧时,每个类型都会单独传入进行计算,最后将计算结果组合成一个联合类型。此功能称为分布式条件类型。比如这样一个联合类型:typeUnion='a'|'b'|'C';如果我们想把其??中的a大写,我们可以这样写:typeUppercaseA=Itemextends'a'?大写<项目>:项目;因为Item是类型参数,出现在条件类型的左边,输入的是联合类型,那么会触发分布特征,分别传入每个类型进行计算,然后得到结果组合成一个联合类型。所以这里Pextendsany的作用就是触发关节类型特征,让这个类型能够正确的处理关节类型。否则,如果关节类型是整体传入的,后面怎么算。这里的Pextendsany可以换成PextendsP,两者效果一样。下面的代码字符串extendskeyofP是什么意思?还真想了想,如果索引类型为{a:1,b:2},keyof的结果就是'a'|'b',如果是数组类型,那么keyof的结果为0|1|'长度'|'toString'|...字符串的结果是什么类型的keyof?突然想起前几天学习的一个知识点:用keyofany代替string|编号|symbol更灵活:而且我试过never的keyof,结果是一样的:所以stringextendskeyofP可以排除any和never的情况!厉害了,居然还能这样区分any和never。所以,我们理清了这种类型的逻辑:这种类型的作用是保留只有A有的索引,让A和B都有的索引可选,让只有B有的索引可选。并且处理了联合类型的情况。如果传入any或never,则不做任何处理,直接返回。我们也从这种类型中学到了很多东西。综上所述,看了@types/react的类型定义,收获颇多:使用infer比Obj[key]更方便提取optionalindex的值,因为前者只需要Obj[Key]extends{xxx?:inferValue:undefined},而后者需要排除value类型为undefined的情况,然后用Exclude去掉type中的undefined。在ts3.0中,采用没有索引类型的索引将返回{}而不是never。如果需要兼容,可以单独判断:keyofObj中的'xxx'。处理索引类型,可以综合使用内置的Pick、Partial、Exclude、Extract等高级类型对索引的各个部分进行处理,然后取交叉类型合并在一起。Pextendsany和PextendsP的作用是触发联合类型的分布特征。只有加上这个处理才能正确处理接头类型。每种类型都会分别传入进行计算,最后将结果合并到联合类型中。stringextendskeyofObj可以确定any和never类型,只有这两种类型带keyof结果是string|编号|符号,包括字符串。而且,我还讲了一个小技巧:ts类型只有在计算的时候才会求值。如果是索引类型,可以使用映射类型的语法来创建相同的索引类型。因为用到了,所以会计算出来,这样才能显示出最终的类型。不得不说,React的类型定义还是比较完善的,兼顾了各种类型的处理和低版本的兼容,我们还是可以从中学到很多东西的。