I'm trying to enforce two generic function parameters matching each other so they can be combined together. The enforcement seems to work but I can't seem to combine the values without an issue from the compiler.
function keys<T extends object>(obj: T) {
return Object.keys(obj) as (keyof T)[];
}
function combineObjs<A extends object, B extends Record<keyof A, number>>(valsA: A, valsB: B) {
// const aggr: Partial<T> = {}
const aggr = { ...valsB }
keys(valsA).forEach( key => {
// Operator '+' cannot be applied to types 'A[keyof A]' and 'B[keyof A]'.
aggr[key] = valsA[key] + valsB[key]
})
return aggr // as Required<T>
}
const result = combineObjs({ foo: 1 }, { foo: 2 })
Since you define A as object, it can contain any property value types like objects, which aren't suitable for +
operator.
And seems we need an approach of accepting any A object but limiting B to numeric properties of A. It uses casting but "matches" the runtime well:
type PickValues<T extends object, V> = {[K in keyof T]: T[K] extends V ? T[K]: never};
function combineObjs<A extends object, B extends Partial<PickValues<A, number>>>(valsA: A, valsB: B) {
const aggr = {} as Record<keyof A, number>;
for(const key in valsA){
if(typeof valsA[key] === 'number'){
aggr[key] = valsA[key] + (valsB[key] ?? 0);
}
}
return aggr as PickValues<A, number>;
}
const result = combineObjs({ foo: 1, boo: 2}, { foo: 2});
console.log(result);