I tried practicing type gymnastics with typescript.And my goal is Implementing mathematical operations on numeric literals.
First of all,I implemented type BuildArray
:
type BuildArray<
Length extends number,
Ele = unknown,
Arr extends unknown[] = []
> = Arr['length'] extends Length
? Arr
: BuildArray<Length, Ele, [...Arr, Ele]>;
type MyArray = BuildArray<3> // type MyArray = [unknown, unknown, unknown]
Then,I implemented type Add
:
type Add<Num1 extends number, Num2 extends number> =
[...BuildArray<Num1>, ...BuildArray<Num2>]['length']
type AddResult = Add<2, 5> // type AddResult = 7
However, when I tried implementing Multiply
based on Add
,it occured error:
type Multiply<Num1 extends number, Num2 extends number, Counter extends number = 0, Result extends number = 0> =
Counter extends Num2?
Result:
Multiply<Num1, Num2, Add<Counter, 1>, Add<Num1, Result>>
type MultiplyResult = Multiply<4, 5> // type MultiplyResult = 20
Though the result is correct, it occured compile error:
Can someone please tell me why this compilation error occurs?
CodePudding user response:
When you are passing Counter
in Add<Counter, 1>
, it's extending the type number
, but it's still unknown number. When it's passed from Add
to BuildArray
, you then have this check:
Arr['length'] extends Length?
and based on that check, you have different expected types Arr
or BuildArray<Length, Ele, [...Arr, Ele]>
which will again have the same issue.
Going back to Add
, in the case of unknown number, TypeScript won't achieve that this length
prop is the prop of an Array
, which is already a number
.
So you get the value correctly, but the type itself is still not sure that this is a number
, because the check Arr['length'] extends Length?
in BuildArray
is not resolved to know that the Arr
is the type that we are trying to access length
from.
I am not sure if this was clear enough, but TL;DR: To achieve what you need with the minimum changes, you need to tell TypeScript that this Add
type will always be a number
instead of relying on TypeScript to learn that by itself.
For example, you can force this info by adding & number
:
type Add<Num1 extends number, Num2 extends number> =
[...BuildArray<Num1>, ...BuildArray<Num2>]['length'] & number
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^
or have any kind of type checks.
CodePudding user response:
This is because here you are extending with number
Counter extends number = 0, Result extends number = 0
and your Add<num1, num2>
type returns a narrowed number.
For example: type AddResult = Add<2, 5> // type AddResult = 7
when you do typeof AddResult
, the output would be 7 (for TS it is unknown) and not number
and that's why the error occured.