当前期刊数: 252
解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 63~68 题。
精读
Unique
实现 Unique<T>
,对 T
去重:
1 |
|
去重需要不断递归产生去重后结果,因此需要一个辅助变量 R
配合,并把 T
用 infer
逐一拆解,判断第一个字符是否在结果数组里,如果不在就塞进去:
1 |
|
那么剩下的问题就是,如何判断一个对象是否出现在数组中,使用递归可以轻松完成:
1 |
|
每次取首项,如果等于 Value
直接返回 true
,否则继续递归,如果数组递归结束(不构成 Arr extends [xxx]
的形式)说明递归完了还没有找到相等值,直接返回 false
。
把这两个函数组合一下就能轻松解决本题:
1 |
|
MapTypes
实现 MapTypes<T, R>
,根据对象 R
的描述来替换类型:
1 |
|
因为要返回一个新对象,所以我们使用 { [K in keyof T]: ... }
的形式描述结果对象。然后就要对 Value 类型进行判断了,为了防止 never
的作用,我们包一层数组进行判断:
1 |
|
但这个解答还有一个 case 无法通过:
1 |
|
我们需要考虑到 Union 分发机制以及每次都要重新匹配一次是否命中 mapFrom
,因此需要抽一个函数:
1 |
|
为什么要 R extends any
看似无意义的写法呢?原因是 R
是联合类型,这样可以触发分发机制,让每一个类型独立判断。所以最终答案就是:
1 |
|
Construct Tuple
生成指定长度的 Tuple:
1 |
|
比较容易想到的办法是利用下标递归:
1 |
|
但在如下测试用例会遇到递归长度过深的问题:
1 |
|
一种解法是利用 minusOne 提到的 CountTo
方法快捷生成指定长度数组,把 1
替换为 unknown
即可:
1 |
|
Number Range
实现 NumberRange<T, P>
,生成数字为从 T
到 P
的联合类型:
1 |
|
以 NumberRange<2, 9>
为例,我们需要实现 2
到 9
的递增递归,因此需要一个数组长度从 2
递增到 9
的辅助变量 U
,以及一个存储结果的辅助变量 R
:
1 |
|
所以我们先实现 LengthTo
函数,传入长度 N
,返回一个长度为 N
的数组:
1 |
|
然后就是递归了:
1 |
|
R
的默认值为 never
非常重要,否则默认值为 any
,最终类型就会被放大为 any
。
Combination
实现 Combination<T>
:
1 |
|
本题和 AllCombination
类似:
1 |
|
还记得这题吗?我们要将字符串变成联合类型:
1 |
|
而本题 Combination
更简单,把数组转换为联合类型只需要 T[number]
。所以本题第一种组合解法是,将 AllCombinations
稍微改造下,再利用 Exclude
和 TrimRight
删除多余的空格:
1 |
|
还有一种非常精彩的答案在此分析一下:
1 |
|
依然利用 T[number]
的特性将数组转成联合类型,再利用联合类型 extends
会分组的特性递归出结果。
之所以不会出现结尾出现多余的空格,是因为 U extends infer U extends string
这段判断已经杜绝了 U
消耗完的情况,如果消耗完会及时返回 never
,所以无需用 TrimRight
处理右侧多余的空格。
至于为什么要定义 A = U
,在前面章节已经介绍过了,因为联合类型 extends
过程中会进行分组,此时访问的 U
已经是具体类型了,但此时访问 A
还是原始的联合类型 U
。
Subsequence
实现 Subsequence<T>
输出所有可能的子序列:
1 |
|
因为是返回数组的全排列,只要每次取第一项,与剩余项的递归构造出结果,|
上剩余项本身递归的结果就可以了:
1 |
|
总结
对全排列问题有两种经典解法:
- 利用辅助变量方式递归,注意联合类型与字符串、数组之间转换的技巧。
- 直接递归,不借助辅助变量,一般在题目返回类型容易构造时选择。
讨论地址是:精读《Unique, MapTypes, Construct Tuple…》· Issue ##434 · dt-fe/weekly
如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)