解决TS题最好的方法就是多练习。这次解读类型挑战中等难度是63~68题。精读Unique实现Unique,T的去重:typeRes=Unique<[1,1,2,2,3,3]>//应该是[1,2,3]typeRes1=Unique<[1,2,3,4,4,5,6,7]>//预期为[1,2,3,4,5,6,7]typeRes2=Unique<[1,'a',2,'b',2,'a']>//预期为[1,"a",2,"b"]typeRes3=Unique<[string,number,1,'a',1,string,2,'b',2,number]>//预期为[string,number,1,"a",2,"b"]typeRes4=Unique<[unknown,unknown,any,any,never,never]>//预期为[unknown,any,never]去重需要不断递归产生去重结果,所以需要一个辅助变量R来配合,用infer将T逐一反汇编,判断第一个字符是否为在结果数组中,如果不存在,则插入:typeUnique=Textends[inferF,...inferRest]?包含扩展为真?Unique:Unique:R那么剩下的问题就是如何判断一个对象是否出现在数组中,使用递归可以轻松完成:typeIncludes=Arrextends[inferF,...inferRest]?等于扩展t后悔吗?true:Includes:false每次取第一项,如果等于Value直接返回true,否则继续递归,如果数组递归结束(不构成Arrextends[xxx]的形式),说明递归还没有找到相等值,直接返回false,结合这两个函数就可以轻松解决这道题://TheanswertothisquestiontypeUnique=Textends[inferF,...inferRest]?包含扩展为真?Unique:Unique:RtypeIncludes=Arrextends[inferF,...inferRest]?等于扩展为真?true:Includes:falseMapTypes实现了MapTypes,根据对象R的描述替换类型:typeStringToNumber={mapFrom:string;//key的值为string的值mapTo:number;//将转换为数字}MapTypes<{iWillBeANumberOneDay:string},StringToNumber>//给出{iWillBeANumberOneDay:number;}因为我们要返回一个新的对象,所以我们使用{[KinkeyofT]:...}来描述结果对象。那么就要判断Value类型了。为了防止never的效果,我们包裹一个数组进行判断:typeMapTypes={[KinkeyofT]:[T[K]]extends[R['mapFrom']]?R['mapTo']:T[K]}但是这个答案还有个case不能通过:MapTypes<{iWillBeNumberOrDate:string},StringToDate|StringToNumber>//给出{iWillBeNumberOrDate:number|日期;}我们需要考虑到Union分配机制,每次是否命中mapFrom都要重新匹配,所以需要抽取一个函数:typeTransform=Rextendsany?T扩展R['mapFrom']?R['mapTo']:never:never为什么Rextendsany看起来毫无意义?原因是R是联合类型,可以触发分配机制,让每个类型独立判断。所以最后的答案是://TheanswertothisquestiontypeMapTypes={[KinkeyofT]:[T[K]]extends[R['mapFrom']]?Transform:T[K]}typeTransform=Rextendsany?T扩展R['mapFrom']?R['mapTo']:never:neverConstructTuple生成指定长度的Tuple:typeresult=ConstructTuple<2>//expecttobe[unknown,unknown]更容易想到的方法是使用下标递归:typeConstructTuple=I['length']extendsL?[]:[unknown,...ConstructTuple]但是在下面的测试用例中会遇到递归太深的问题length:ConstructTuple<999>//Typeinstantiationisexcessivelydeep并且可能是无限的一种方案是使用minusOne提到的CountTo方法快速生成一个指定长度的数组,将1替换为unknown://本题的答案类型ConstructTuple=CountTo<`${L}`>typeCountTo=Textends`${inferFirst}${inferRest}`?CountTo[keyofN&First]>:CounttypeN={'0':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T]'1':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知]'2':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知]'3':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知,未知]'4':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知,未知,未知]'5':[...T,...T,...T,...T,...T,。..T,...T,...T,...T,...T,未知,未知,未知,未知,未知]'6':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知,未知,未知,未知,未知n]'7':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知,未知,未知,未知,未知,未知]'8':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知,未知,未知,未知,未知,未知,未知]'9':[...T,...T,...T,...T,...T,...T,...T,...T,...T,...T,未知,未知,unknown,unknown,unknown,unknown,unknown,unknown,unknown]}NumberRange实现NumberRange,生成从T到P的联合类型的数字:typeresult=NumberRange<2,9>//|2|3|4|5|6|7|8|9以NumberRange<2,9>为例,我们需要实现从2递增到9的递归,所以需要一个数组长度从2递增到9的辅助变量U,和一个存储结果的辅助变量R:typeNumberRange所以我们先实现LengthTo函数,传入长度N,返回一个长度N的数组:typeLengthTo=R['length']extendsN?R:LengthTo然后是递归的://本题答案类型NumberRange,Rextendsnumber=never>=U['length']extendsP?(R|U['length']):(NumberRange)R默认值永远不是很重要,否则默认值为any,最终类型会放大为any组合实现Combination://预期为`"foo"|“酒吧”|“巴兹”|“富吧”|"foobarbaz"|“富巴兹”|"foobaz酒吧"|“酒吧富”|“酒吧富巴兹”|“酒吧巴兹”|"barbazfoo"|“巴兹富”|"bazfoo酒吧"|“巴兹酒吧”|"bazbarfoo"`typeKeys=Combination<['foo','bar','baz']>这道题类似于AllCombination:typeAllCombinations_ABC=AllCombinations<'ABC'>//shouldbe''|'一个'|'乙'|'C'|'AB'|'交流'|'广管局'|'公元前'|'CA'|'CB'|'美国广播公司'|'ACB'|'BAC'|'BCA'|'驾驶室'|'CBA'还记得这个问题吗?我们想把字符串变成联合类型:typeStrToUnion=Sextends`${inferF}${inferR}`?女|StrToUnion:never而这道题Combination更简单,把数组转成union类型只需要是T[number]即可。所以这道题的第一种组合解法是稍微修改AllCombinations,然后用Exclude和TrimRight删除多余的空格://TheanswertothisquestiontypeAllCombinations=[U]延伸[从不]?'':''|{[KinU]:`${K}${AllCombinations>}`}[U]typeTrimRight=Textends`${inferR}`?TrimRight:TtypeCombination=TrimRight,''>>这里还有一个很精彩的答案分析一下://这道题的答案typeCombination=UextendsinferUextendsstring?`${U}${组合>}`|你:从不;还是利用T[number]的特性将数组转为联合类型,再利用联合类型extendstogroup的特性递归得到结果。之所以最后不会有多余的空格,是因为UextendsinferUextendsstring的判断已经排除了U用完的情况。如果用完了,会及时返回never,所以右边多余的空格就不用用TrimRight来处理了。至于为什么要定义A=U,在上一章已经介绍过了,因为在jointtypeextends过程中会进行分组。此时访问的U已经是特定的类型,但是此时访问A的还是原来的联合类型U。Subsequence实现Subsequence以输出所有可能的子序列:typeA=Subsequence<[1,2]>//[]|[1]|[2]|[1,2]因为返回的是数组的全数组,每次只要取第一项,然后递归剩余的项构造结果,剩余项的递归结果放在|上会没问题的://这个问题的答案类型Subsequence=Textends[inferF,...推断R扩展number[]]?(Subsequence|[F,...Subsequence]):TSummary全排列问题有两种经典解法:使用辅助变量递归,注意联合类型在字符串和数组之间转换的技巧。直接递归,不带辅助变量,一般在题目的返回类型容易构造时选择。讨论地址是:Jingdu《Unique, MapTypes, Construct Tuple...》·Issue#434·dt-fe/weekly想参与讨论的请戳这里,每周都有新话题,周末或周一发布。前端精读——帮你过滤靠谱的内容。关注前端精读微信公众号版权声明:免费转载-非商业-非衍生保留属性(CreativeCommons3.0License)