TypeScript:是否有将字符串字面量类型转换为数字类型的方法?

13

是否有可能编写一个实用工具类型 Number<T>,它接受可以转换为数字的字符串字面类型,如果不能,则返回一个never类型?

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

我想事先回答一个关于为什么我想这样做的问题:

我提出这个问题的原因是我正在尝试编写一个“添加”实用程序类型,用于加上这个数字。

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<'3','10'>

3个回答

8

TypeScript 4.8将允许将字符串字面量类型转换为其他类型,例如numberbigintboolean。请参见此PR

通过创建一个名为ParseInt的实用类型,我们可以将字符串字面量“转型”为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<"-13">  // -13
type T3 = ParseInt<"abc">  // never

这里是其他类型转换的示例:
type ParseBigint<T> = T extends `${infer N extends bigint}` ? N : never

type T4 = ParseBigint<"1">    // 1n
type T5 = ParseBigint<"100">  // 100n
type T6 = ParseBigint<"abc">  // never


type ParseBoolean<T> = T extends `${infer N extends boolean}` ? N : never

type T7 = ParseBoolean<"true">   // true
type T8 = ParseBoolean<"false">  // false
type T9 = ParseBoolean<"abc">    // never

在线编写


8
没有办法将任意字符串文本类型转换为数字文本类型(我通常称之为StringToNumber<T>)。最近有一个请求在microsoft/TypeScript#47141,要求支持此功能,但被拒绝了。他们不想支持这个功能。还有一个仍未解决的问题microsoft/TypeScript#26382,要求支持文本类型的任意数学运算,包括请求StringToNumber<T>,也许还有一些希望?但我不指望它。


如果您只关心小于1000的非负整数(由于尾递归消除的限制),则可以通过元组操作自己实现,类似于您正在执行的Add操作:

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 链接


1
嘿,非常感谢! 您是否知道我如何创建类似于“Add”的实用程序类型“Minus”? 我一直在尝试实现它,但无法弄清楚您可以如何做到这一点。 - Joji
1
你可以做这个,我猜?我感觉有点不好,因为这只是一个评论,而且它没有进入任何真正的答案; 你可能想要为此打开一个新问题,这样我就可以写一个真正的答案。或者至少接受这个答案 ‍♂️ - jcalz
1
哇,我一定会为此开一个新的问题,以便学习您是如何做到的!这真的让我大吃一惊,因为我认为在 TypeScript 中实现 minus 是不可能的,因为您可以轻松地向数组中添加更多元素,但似乎没有简单的方法从数组中弹出元素。 - Joji
我为此开了一个新问题 https://dev59.com/T8Pra4cB1Zd3GeqPazPA - Joji

-1

您可以手动创建一个地图,如下所示:

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

然后执行这个操作:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接