相信大家在看同事写的代码或者优秀开源库的代码的时候,一定见过各种风骚的TS写法,不花一点时间是看不懂的。如果是我们,你可能随便写个any就完了,但是当项目规模变大的时候,你会发现这些TS操作真的很重要,因为它可以很好的帮你做静态类型校验。今天介绍一下我在其他开源库中看到的一个。既花哨又实用的TS型——TS型过滤器自我介绍TS型过滤器,英文名称(自己取的)是FilterConditionally,这就是它的完整外观👇typeFilterConditionally=Pick;虽然看起来很复杂,但实际上非常有用。它可以从一个对象类型中过滤出你想要的,比如:interfaceExample{a:string;//?b:string;//?c:number;//?d:boolean;//?}typeNewType=FilterConditionally/*NewType最后的结果是:{a:string;b:string}*/相信大家已经有了这个类型的函数,你也想了解一下。没关系。接下来,我会由内而外,一步一步的介绍,让大家充分了解。不懂就来喷我(我开玩笑的~)一步一步的介绍涉及到很多知识点。恐怕有些不熟悉TS的同学会一头雾水。先介绍一些常见的基础知识点。朋友可以直接调整keyof关键字。keyof的名字是一个索引类型的查询操作符,其作用正如其字面意思一样直截了当:xxinterfaceExample的键值{a:string;b:string;c:number;d:boolean;}typeKeys=keyofExample//相当于typeKeys='a'|'b'|'c'|'d'可以简单理解keyof为JavaScript中的Object.keysin关键字,可以遍历列表引用类型,例如:typeKeys='a'|'b'|'c'|'d'typeObj={[TinKeys]:string;//遍历Keys,将每个key赋给string类型}/*相当于typeObj={a:string;b:string;c:string;d:string;}*/in可以简单理解为JavaScript中in的函数for...inConditional第二个知识点是条件判断,如:interfaceA{}interfaceBextendsA{}//BinheritsfromA//B是否继承自A?如果是,则为数字类型;如果不是,则为字符串类型typeC=BextendsA?number:string//等同于typeC=number//A是否继承自B?如果是,则为数字类型;如果不是,则为字符串类型typeD=AextendsB?number:string//等同于typeD=string你可以简单理解为AextendsB?我不会过多介绍泛型类型。不太了解的可以直接看TS文档——Generictypes[1]刚刚介绍完开饭的“开胃菜”,趁热打铁看看在一个简单的类型typeMarkUnwantedTypesAsNever={[KinkeyofSource]:Source[K]extendsCondition?K:never}一句话,这个类型的作用就是遍历一个对象类型,将不需要的类型标记为never。比如🌰interfaceExample{a:string;//?b:string;//?c:number;//?d:boolean;//?}//我只想要string类型的key在示例类型,非字符串标记为nevertypeMyType=MarkUnwantedTypesAsNever/*等同于:typeMyType={a:'a';b:'b';c:never;d:never;}*/说说细节section,[KinkeyofExample]遍历Example的对象类型,然后用条件判断Example[K]extendsstring?K:从不给对应的键值赋值。假设要遍历的第一个key的值为a,那么Example[K]]=Example[a]=string,此时是stringextendsstring?'a':never,string必须继承自string,所以才会出现这样的结果这时候大家就奇怪了,为什么要把类型改成so呢??最后的结果不就是我们要得到的吗{a:string;b:string}类型?别着急,后面还有其他操作。再看下索引访问接口属性的一个小知识点typeValue={name:"zero2one"}["name"]//相当于typeValue="zero2one"可以简单理解成对应的值JavaScript中访问对象的某个key,TS中还有一种情况:typeValue={name:"zero2one";age:23}["name"|"age"]//等价于typeValue="zero2one"|23和never的键值无法访问:typeValue={name:"zero2one";age:never}["name"|"age"]//等同于typeValue="zero2one"所以我们可以看更复杂的类型typeMarkUnwantedTypesAsNever={[KinkeyofSource]:Source[K]extendsCondition?K:never}[keyofSource]我们巧妙的使用keyof关键字遍历访问所有接口的Attribute//借用刚才例子的结果typeMyType={a:'a';b:'b';c:never;d:never;}['a'|'b'|'c'|'d']/*等待price:typeMyType='a'|'b'*/至此,我们所做的就是:在目标对象类型中筛选出想要的类型的键值,不用着急,离成功就差一步了最后出现的是Pick。这种类型是在TS中内置的。让我们简单了解一下它的作用。要理解它的实现,只需要知道Pick的作用就是过滤掉类型T中指定的某些属性即可。举个简单的例子:interfaceA{a:1;b:2;c:3;d:4;}typeC=Pick//相当于typeC={a:1;c:3}是的,就这么简单,好吧,让我们看看最终的BOSS,然后最后从Source中选择中相应的属性。回到本文的具体例子,上面已经获取到图中红框内的值为typeMyType='a'|'b',然后在最后选择它。string;b:string;c:number;d:boolean;}//上面得到的结果typeMyType='a'|'b'typeResult=Pick//相当于typeResult={a:string;b:string}//----以上等价于----//interfaceExample{a:string;//?b:string;//?c:number;//?d:boolean;//?}typeNewType=FilterConditionally/*NewType最终结果为:{a:string;b:string}*/这就是文章开头的结果获取实际应用示例的全过程。正如本文标题所说,TS类型过滤在很多优秀的开源库中非常常见,比如大家熟悉的React:typeElementType={[KinkeyofJSX.IntrinsicElements]:PextendsJSX.IntrinsicElements[K]?K:从不}[keyofJSX.IntrinsicElements][keyofJSX.IntrinsicElements]元素]|ComponentType;最后,开源库中类似TS类型过滤的场景太多了,希望大家以后遇到的时候能够轻松理解。如果你是看屏幕前就是后端,说不定你也可以在后端开源框架的源码中看到~