精读《MinusOne, PickByType, StartsWith...》

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

解决 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,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值