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