当前期刊数: 246
解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 17~24 题。
精读
Permutation
实现 Permutation
类型,将联合类型替换为可能的全排列:
1 |
|
看到这题立马联想到 TS 对多个联合类型泛型处理是采用分配律的,在第一次做到 Exclude
题目时遇到过:
1 |
|
所以这题如果能 “递归触发联合类型分配率”,就有戏解决啊。但触发的条件必须存在两个泛型,而题目传入的只有一个,我们只好创造第二个泛型,使其默认值等于第一个:
1 |
|
这样对本题来说,会做如下展开:
1 |
|
对于 Permutation<'A', 'A' | 'B' | 'C'>
来说,排除掉对自身的组合,可形成 'A', 'B'
,'A', 'C'
组合,之后只要再递归一次,再拼一次,把已有的排除掉,就形成了 A
的全排列,以此类推,形成所有字母的全排列。
这里要注意两点:
- 如何排除掉自身?
Exclude<T, P>
正合适,该函数遇到T
在联合类型P
中时,会返回never
,否则返回T
。 - 递归何时结束?每次递归时用
Exclude<U, T>
留下没用过的组合,最后一次组合用完一定会剩下never
,此时终止递归。
1 |
|
验证一下答案,首先展开 Permutation<'A', 'B', 'C'>
:
1 |
|
我们再展开第一行 Permutation<'B' | 'C'>
:
1 |
|
再展开第一行的 Permutation<'C'>
:
1 |
|
此时已经完成全排列,但我们还要处理一下 Permutation<never>
,使其返回 []
并终止递归。那为什么要用 [T] extends [never]
而不是 T extends never
呢?
如果我们用 T extends never
代替本题答案,输出结果是 never
,原因如下:
1 |
|
理论上相同的代码,为什么用泛型后输出就变成 never
了呢?原因是 TS 在做 T extends never ?
时,会对联合类型进行分配,此时有一个特例,即当 T = never
时,会跳过分配直接返回 T
本身,所以三元判断代码实际上没有执行。
[T] extends [never]
这种写法可以避免 TS 对联合类型进行分配,继而绕过上面的问题。
Length of String
实现 LengthOfString<T>
返回字符串 T 的长度:
1 |
|
破解此题你需要知道一个前提,即 TS 访问数组类型的 [length]
属性可以拿到长度值:
1 |
|
也就是说,我们需要把 'abc'
转化为 ['a', 'b', 'c']
。
第二个需要了解的前置知识是,用 infer
指代字符串时,第一个指代指向第一个字母,第二个指向其余所有字母:
1 |
|
那转换后的数组存在哪呢?类似 js,我们弄第二个默认值泛型存储即可:
1 |
|
思路就是,每次把字符串第一个字母拿出来放到数组 N
的第一项,直到字符串被取完,直接拿此时的数组长度。
Flatten
实现类型 Flatten
:
1 |
|
此题一看就需要递归:
1 |
|
这道题看似答案复杂,其实还是用到了上一题的套路:递归时如果需要存储临时变量,用泛型默认值来存储。
本题我们就用 Result
这个泛型存储打平后的结果,每次拿到数组第一个值,如果第一个值不是数组,则直接存进去继续递归,此时 T
自然是剩余的 Rest
;如果第一个值是数组,则将其打平,此时有个精彩的地方,即 ...Start
打平后依然可能是数组,比如 [[5]]
就套了两层,能不能想到 ...Flatten<Start>
继续复用递归是解题关键。
Append to object
实现 AppendToObject
:
1 |
|
结合之前刷题的经验,该题解法很简单,注意 K in Key
可以给对象拓展某些指定 Key:
1 |
|
当然也有不用 Obj &
的写法,即把原始对象和新 Key, Value 合在一起的描述方式:
1 |
|
Absolute
实现 Absolute
将数字转成绝对值:
1 |
|
该题重点是把数字转成绝对值字符串,所以我们可以用字符串的方式进行匹配:
1 |
|
为什么不用 T extends
来判断呢?因为 T
是数字,这样写无法匹配符号的字符串描述。
String to Union
实现 StringToUnion
将字符串转换为联合类型:
1 |
|
还是老套路,用一个新的泛型存储答案,递归即可:
1 |
|
当然也可以不依托泛型存储答案,因为该题比较特殊,可以直接用 |
:
1 |
|
Merge
实现 Merge
合并两个对象,冲突时后者优先:
1 |
|
这道题答案甚至是之前题目的解题步骤,即用一个对象描述 + keyof
的思维:
1 |
|
只要知道 in keyof
支持元组,值部分用 extends
进行区分即可,很简单。
KebabCase
实现驼峰转横线的函数 KebabCase
:
1 |
|
还是老套路,用第二个参数存储结果,用递归的方式遍历字符串,遇到大写字母就转成小写并添加上 -
,最后把开头的 -
干掉就行了:
1 |
|
分开写就非常容易懂了,首先 KebabCase
每次递归取第一个字符,如何判断这个字符是大写呢?只要小写不等于原始值就是大写,所以判断条件就是 Lowercase<F> extends F
的 false 分支。然后再写个函数 RemoveFirstHyphen
把字符串第一个 -
干掉即可。
总结
TS 是一门编程语言,而不是一门简单的描述或者修饰符,很多复杂类型问题要动用逻辑思维来实现,而不是查查语法就能简单实现。
讨论地址是:精读《Permutation, Flatten, Absolute…》· Issue ##426 · dt-fe/weekly
如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)