Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

「重学TS 2.0 」TS 练习题第三十三题 #52

Open
semlinker opened this issue Sep 22, 2021 · 5 comments
Open

「重学TS 2.0 」TS 练习题第三十三题 #52

semlinker opened this issue Sep 22, 2021 · 5 comments

Comments

@semlinker
Copy link
Owner

实现一个 ToNumber 工具类型,用于实现把数值字符串类型转换为数值类型。具体的使用示例如下所示:

type ToNumber<T extends string> = // 你的实现代码

type T0 = ToNumber<"0">; // 0
type T1 = ToNumber<"10">; // 10
type T2 = ToNumber<"20">; // 20

请在下面评论你的答案

@xiaoYuanDun
Copy link

xiaoYuanDun commented Sep 23, 2021

首先利用 TS 模板字符串把默认数组 S 的长度转为字符串,之后那这个字符串和 T 比较,若相等,表示当前 S 的长度和传入的字符串所对应的数值相等 (如,"15" --> 15),否则增加 S 的长度,进行下一次 ToNumber 判断

不过这种方式可能会出现一个警告:Type instantiation is excessively deep and possibly infinite. ts(2589),因为字符串过大时,可能会导致递归次数变多,个人认为可以忽略此错误

基本上,这种和数组有关系的类型,都需要构建一个辅助数组来进行判断

type ToNumber<T extends string, S extends any[] = [], L extends number = S['length']> = 
  `${L}` extends T ? L : ToNumber<T, [...S, 1]>

//  用例
type T0 = ToNumber<"0">; // 0
type T1 = ToNumber<"10">; // 10
type T2 = ToNumber<"20">; // 20

@xiaoYuanDun
Copy link

一个新的思路,感觉有点复杂了,不过比之前的最大限制提升了一些

// 10进制数,递归数不超过10,解决了递归次数过多的报错问题
type GetNumber<T extends any, S extends any[] = [], L extends number = S['length']> = 
  `${L}` extends T ? L : GetNumber<T, [...S, 1]>

// 字符串转反向数组, 方便从后向前构建目标数组, 如: '1234' -> ['4', '3', '2', '1']
type StrToReverseArr<T extends string> = 
  T extends `${infer R1}${infer R2}` ? R2 extends '' ? [R1] : [...StrToReverseArr<R2>, R1] : []

/**
 * 辅助矩阵, 用于构建当前位数的值, 那上面的 ['4', '3', '2', '1'] 来说, 相当于:
 * 
 * 个位数: 4 --> 构建一个长度为 4*0001 的数组, Matrix<[1], 4>
 * 十位数: 3 --> 构建一个长度为 3*0010 的数组, Matrix<Matrix<[1], 10>, 3> 
 * 百位数: 2 --> 构建一个长度为 2*0100 的数组, Matrix<Matrix<Matrix<[1], 10>, 10>, 2> 
 * 千位数: 1 --> 构建一个长度为 1*1000 的数组, Matrix<Matrix<Matrix<Matrix<[1], 10>, 10>, 10> , 1> 
 */
type Matrix<T extends any[], I extends number> = [
    [],
    [...T],
    [...T, ...T],
    [...T, ...T, ...T],
    [...T, ...T, ...T, ...T],
    [...T, ...T, ...T, ...T, ...T],
    [...T, ...T, ...T, ...T, ...T, ...T],
    [...T, ...T, ...T, ...T, ...T, ...T, ...T],
    [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T],
    [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T],
    [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T],
][I]

// 生成并合并所有位数的数组
type GetCurArr<
    T extends string,                       //  初始参数, 只在第一次执行时生效, 后面用空字符串 '' 占位
    A extends any[] = StrToReverseArr<T>,   //  反转后的数组, 只在第一次时通过计算得到, 后面都是递归传入
    D extends any[] = [1],                  //  当前位数的单位长度, 核心是通过这个构建每位所对应的数组, 然后合并他们
> = A extends [infer R1, ...infer R2] 
  ? [...GetCurArr<'', R2, Matrix<D, 10>>, ...Matrix<D, GetNumber<R1>>] 
  : []

type ToNumber<T extends string> = GetCurArr<T>['length']

//  用例
type T0 = ToNumber<"123">; // 123
type T1 = ToNumber<"10">; // 10
type T2 = ToNumber<"999">; // 999

// type T3 = GetCurArr<"1999">; 
// 还是没有完全解决递归的问题, 超过 1000 会报 "Type produces a tuple type that is too large to represent.(2799)"

@zhaoxiongfei
Copy link

type ToNumber<T extends string, A extends any[] = []> = `${A["length"]}` extends T
  ? A["length"]
  : ToNumber<T, [...A, '']>;

type T0 = ToNumber<"0">; // 0
type T1 = ToNumber<"10">; // 10
type T2 = ToNumber<"20">; // 20

思路: ts里运算很匮乏,并没有直接的数字运算,这里巧妙的利用了数组长度来实现,通过递归构造数组,使得构造出来的数组长度和期望的匹配,主要要把数组长度通过字符串模板的方式转换成字符串。否则永远匹配不成功

@zhengyimeng
Copy link

type ToNumber<
  T extends string,
  A extends any[] = []
> = `${A['length']}` extends T ? A['length'] : ToNumber<T, [...A, T]> 

type T0 = ToNumber<"0">; // 0
type T1 = ToNumber<"10">; // 10
type T2 = ToNumber<"20">; // 20

@zhengyimeng
Copy link

// 这个O就算是个数字也可以扩展才对,这就是最佳解法
type ToNumber<T extends string> = T extends `${infer O extends number}` ? O : never

type T0 = ToNumber<"0">; // 0
type T1 = ToNumber<"10">; // 10
type T2 = ToNumber<"20">; // 20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants