解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 33~40 题。
精读
MinusOne
用 TS 实现 MinusOne 将一个数字减一:
type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
TS 没有 “普通” 的运算能力,但涉及数字却有一条生路,即 TS 可通过 ['length'] 访问数组长度,几乎所有数字计算都是通过它推导出来的。
这道题,我们只要构造一个长度为泛型长度 -1 的数组,获取其 ['length'] 属性即可,但该方案有一个硬伤,无法计算负值,因为数组长度不可能小于 0:
// 本题答案
type MinusOne<T extends number, arr extends any[] = []> = [
...arr,
''
]['length'] extends T
? arr['length']
: MinusOne<T, [...arr, '']>
该方案的原理不是原数字 -1,而是从 0 开始不断加 1,一直加到目标数字减一。但该方案没有通过 MinusOne<1101> 测试,因为递归 1000 次就是上限了。
还有一种能打破递归的思路,即:
type Count = ['1', '1', '1'] extends [...infer T, '1'] ? T['length'] : 0 // 2
也就是把减一转化为 extends [...infer T, '1'],这样数组 T 的长度刚好等于答案。那么难点就变成了如何根据传入的数字构造一个等长的数组?即问题变成了如何实现 CountTo<N> 生成一个长度为 N,每项均为 1 的数组,而且生成数组的递归效率也要高,否则还会遇到递归上限的问题。
网上有一个神仙解法,笔者自己想不到,但是可以拿出来给大家分析下:
type CountTo<
T extends string,
Count extends 1[] = []
> = T extends `${infer First}${infer Rest}`
? CountTo<Rest, N<Count>[keyof N & First]>
: Count
type N<T extends 1[] = []> = {
'0': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T]
'1': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1]
'2': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1]
'3': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1]
'4': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1]
'5': [
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
1,
1,
1,
1,
1
]
'6': [
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
1,
1,
1,
1,
1,
1
]
'7': [
...T,
...T,
...T,
...T,
...T,

本文深入解析TypeScript中的数字操作,如MinusOne、PickByType、StartsWith和EndsWith等挑战,讲解如何利用类型系统进行数字计算和字符串处理。同时,探讨了如何实现对象的可选和必选键转换,以及对象所有键变为可写。文章揭示了TypeScript中的高级类型技巧和递归解决方案。
最低0.47元/天 解锁文章
169

被折叠的 条评论
为什么被折叠?



