TypeScript: is there a way to convert a string literal type to a number type?
Is it possible to write a utility type Number<T>
which accepts a string literal type that can be converted to a number, if not it returns a never
type?
type Five = Number<'5'> // `Five` is of the type number 5
And I just wanted to preemptively answer the question about why I want to do this:
The reason I am asking this question is that I am trying to write an Add
utility type which adds the number
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']
Right now it works
type Answer = Add<3,10> // Answer is 13
But but it only accepts the number
type. I wanted to make it also doesn't accept string
type so that this also works type Answer = Add<'3','10'>
Solution 1:
No, there is no way to turn an arbitrary string literal type to a numeric literal type (I generally call this StringToNumber<T>
). There was a recent request at microsoft/TypeScript#47141 asking for this, which was declined. It's not something they care to support. There is a still-open issue at microsoft/TypeScript#26382 asking for support for arbitrary mathematics on literal types, which includes asking for StringToNumber<T>
; maybe there's still some hope? But I wouldn't count on it.
If all you care about is non-negative whole numbers less than about 1000 (due to restrictions in recursion even with tail-call elimination) then you can implement it yourself with tuple manipulation, similar to how you're doing Add
:
type StringToNumber<T extends string, A extends any[] = []> =
T extends keyof [0, ...A] ? A['length'] : StringToNumber<T, [0, ...A]>
And you can see it work:
type Thirteen = StringToNumber<"13">;
// type Thirteen = 13
This is fragile in the same way as Add
... if you pass unexpected things you might get slow compiler performance or errors:
// type Nope = Add<0.4, 10>
// Type instantiation is excessively deep and possibly infinite.(2589)
So you could try to limit inputs to valid numeric strings:
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)
So that works.
Still I don't know that I'd recommend anything like this unless there's a very good use case for it. The Add
utility type in and of itself isn't something the TS team thinks it's worth supporting (which might be why ms/TS#47141 was declined).
Playground link to code