TypeScript:有没有办法将字符串文字类型转换为数字类型?

Joj*_*oji 14 typescript

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

type Five = Number<'5'> // `Five` is of the type number 5
Run Code Online (Sandbox Code Playgroud)

我只是想先回答一下我为什么要这样做的问题:

我问这个问题的原因是我正在尝试编写一个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']
Run Code Online (Sandbox Code Playgroud)

现在它有效

type Five = Number<'5'> // `Five` is of the type number 5
Run Code Online (Sandbox Code Playgroud)

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

Tob*_* S. 13

TypeScript 4.8 将允许将字符串文字类型转换为其他类型,例如number,bigintboolean。请参阅此公关

通过创建实用程序类型,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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

操场


jca*_*alz 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]>
Run Code Online (Sandbox Code Playgroud)

你可以看到它的工作原理:

type Thirteen = StringToNumber<"13">;
// type Thirteen = 13
Run Code Online (Sandbox Code Playgroud)

这是脆弱的,就像Add......如果你传递了意想不到的东西,你可能会得到编译器性能缓慢或错误:

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

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

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)
Run Code Online (Sandbox Code Playgroud)

这样就可以了。


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

Playground 代码链接