Home > OS >  Why doesn't the 'Add' type satisfy the constraint 'number'?
Why doesn't the 'Add' type satisfy the constraint 'number'?

Time:09-27

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:

enter image description here

Playground link

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.

Playground link

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.

  • Related