【问题标题】:TypeScript: is there a way to convert a string literal type to a number type?TypeScript:有没有办法将字符串文字类型转换为数字类型?
【发布时间】:2022-06-18 22:18:55
【问题描述】:

是否可以编写一个实用程序类型Number<T>,它接受可以转换为数字的字符串文字类型,如果不是,它返回never 类型?

type Five = Number<'5'> // `Five` is of the type number 5

我只是想先发制人地回答我为什么要这样做的问题:

我问这个问题的原因是我正在尝试编写一个 Add 实用程序类型来添加数字

type createArray<Len, Ele, Arr extends Ele[] = []> =  Arr['length'] extends Len ? Arr : createArray<Len, Ele, [Ele, ...Arr]>

type Add<A extends number, B extends number> = [...createArray<A, 1>, ...createArray<B, 1>]['length']

现在可以了

type Answer = Add<3,10> // Answer is 13

但是它只接受number 类型。我想让它也不接受string 类型,这样它也可以工作type Answer = Add&lt;'3','10'&gt;

【问题讨论】:

    标签: typescript


    【解决方案1】:

    不,没有办法将任意字符串文字类型转换为数字文字类型(我通常称之为StringToNumber&lt;T&gt;)。最近在microsoft/TypeScript#47141 上提出了一个请求,但被拒绝了。这不是他们想要支持的东西。 microsoft/TypeScript#26382 有一个尚未解决的问题,要求支持对文字类型的任意数学,其中包括要求 StringToNumber&lt;T&gt;;也许还有一些希望?但我不会指望它。


    如果您只关心小于约 1000 的非负整数(由于递归的限制,即使使用 tail-call elimination),那么您可以使用 tuple 操作自己实现它,类似于您正在做的 @ 987654330@:

    type StringToNumber<T extends string, A extends any[] = []> =
      T extends keyof [0, ...A] ? A['length'] : StringToNumber<T, [0, ...A]>
    

    你可以看到它起作用了:

    type Thirteen = StringToNumber<"13">;
    // type Thirteen = 13
    

    这与Add 一样脆弱...如果您传递了意想不到的东西,您可能会遇到编译器性能下降或错误:

    // type Nope = Add<0.4, 10>
    // Type instantiation is excessively deep and possibly infinite.(2589)
    

    因此您可以尝试将输入限制为有效的数字字符串:

    type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "";
    type NonZero = Exclude<Digit, "0" | "">
    type LessThanAThousand = "0" | `${NonZero}${Digit}${Digit}`
    
    type StringToNumber<T extends LessThanAThousand, A extends any[] = []> =
      T extends LessThanAThousand ? T extends keyof [0, ...A] ?
      A['length'] : StringToNumber<T, [0, ...A]> : never;
    
    type Oops = StringToNumber<"0.4"> // error
    // ----------------------> ~~~~~
    // Type '"0.4"' does not satisfy the constraint 'LessThanAThousand'.(2344)
    

    这样就行了。


    我仍然不知道我会推荐这样的东西,除非有一个非常好的用例。 Add 实用程序类型本身并不是 TS 团队认为值得支持的东西(这可能是 ms/TS#47141 被拒绝的原因)。

    Playground link to code

    【讨论】:

    • 嘿,非常感谢!您是否知道我如何创建类似于Add 的实用程序类型Minus?我一直在努力实现这一目标,但不知道如何做到这一点。
    • 你可以this 我猜?我觉得这只是为了发表评论而在任何地方都没有真正的答案,这让我感到很糟糕;您可能想为此打开一个新问题,以便我可以写一个实际答案。或者至少接受这个?‍♂️
    • 哇,我一定会为它开一个新问题,这样我就可以了解你是如何做到的!这真的让我大吃一惊,因为我认为实现 minus 是不可能的,因为您可以轻松地将更多元素添加到数组中,但在 TypeScript 中从数组中弹出元素似乎并不容易。
    • 我为它开了一个新问题stackoverflow.com/questions/70536229/…
    【解决方案2】:

    您可以像这样手动创建地图:

    type MapStrNum = {
      "1": 1;
      "2": 2;
    };
    

    然后这样做:

    type One = MapNumStr['1'] // One will be 1
    

    【讨论】:

      【解决方案3】:

      TypeScript 4.8 将允许将字符串文字类型转换为其他类型,例如 numberbigintboolean。看到这个PR

      通过创建一个实用类型ParseInt,我们可以“cast”将字符串文字转换为number 类型。

      type ParseInt<T> = T extends `${infer N extends number}` ? N : never
      
      type T0 = ParseInt<"1">    // 1
      type T1 = ParseInt<"100">  // 100
      type T2 = ParseInt<"abc">  // never
      

      以下是其他类型转换的示例:

      type ParseBigint<T> = T extends `${infer N extends bigint}` ? N : never
      
      type T3 = ParseBigint<"1">    // 1n
      type T4 = ParseBigint<"100">  // 100n
      type T5 = ParseBigint<"abc">  // never
      
      
      type ParseBoolean<T> = T extends `${infer N extends boolean}` ? N : never
      
      type T6 = ParseBoolean<"true">   // true
      type T7 = ParseBoolean<"false">  // false
      type T8 = ParseBoolean<"abc">    // never
      

      Playground

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-27
        • 2019-02-09
        • 2023-03-08
        • 2018-08-16
        • 2019-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多