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.
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).