Search code examples
typescripttype-level-computation

Adding numbers at type level in TypeScript


Suppose I have this type:

type NS = {
    readonly A: 4;
    readonly B: 4;
    readonly Op: 2;
    readonly Cin: 1;
}

Then how can I statically know, with a type, that the sum of these ints is 11? I.e.,

type S = SumUp<NS> // how to implement SumUp so that S is 11?

I am using ts-arithmetic, which provides a handy Add<N, M> helper type, but I can't seem to find a way to recursively loop over all keys of NS and accumulate the results.


Solution

  • If you don't have that many keys, you could always hardcode the adds,

    type Summed = Add<NS["A"], Add<NS["B"], Add<NS["Op"], NS["Cin"]>>>;
    

    otherwise, you may have to convert a union to a tuple, from this question. Here's one such implementation using an accumulator (it's faster because it is tail call optimized):

    type _SumUp<T extends Record<string, number>, Keys, Result extends number = 0> =
        Keys extends [infer First extends keyof T, ...infer Rest]
            ? _SumUp<T, Rest, Add<Result, T[First]>>
            : Result;
    type SumUp<T extends Record<string, number>> = _SumUp<T, TuplifyUnion<keyof T>>;
    

    Internally, we "loop over" each of the keys and add the value to the result. Then the SumUp type is really just a wrapper around this internal helper, giving it T and its keys as a tuple.

    If this doesn't fancy you because of the use of TuplifyUnion (and all its problems as described in the linked question), you could still do it:

    //@ts-ignore Type instantiation is excessively deep and possibly infinite.(2589)
    type SumUp<T extends Record<string, number>, K extends keyof T = keyof T> = [keyof T] extends [never] ? 0 : K extends K ? Add<T[K], SumUp<Omit<T, K>>> : never;
    

    However, TS will generate an error, which I have not found a way around yet. So I have just resorted to ignoring it since it works for the inputs we give. This method is also a lot slower for TS to compute the result of (it is noticeably slower with a large delay).

    Playground (hardcoded)

    Playground (using TuplifyUnion)

    Playground (no TuplifyUnion)