Home > Mobile >  subtracting identical object values typescript
subtracting identical object values typescript

Time:02-27

I would to know how to subtract identical object values in typescript using Generics and type-safe, based on: subtracting identical object values javascript

    const subtractObjects = <T extends Record<String, number>>(objA: T, objB: T): T =>
      Object.keys(objA).reduce((a, k) => {
        a[k] = objA[k] - objB[k];
        return a;
      }, {});

I receive the error:

Type '{}' is not assignable to type 'T'.
  '{}' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Record<string, number>'.

CodePudding user response:

When you have a generic type parameter T constrained to Record<string, number>, it doesn't necessarily mean that every property of T will have a value type that's exactly number. It could well be something that extends number, such as a numeric literal type like 123 or a union of such types like 0 | 1. (See Why can't I return a generic 'T' to satisfy a Partial<T>? for a related discussion.) For example:

type Money = {
  denomination: 1 | 5 | 10 | 20 | 50 | 100
}
const twenty: Money = { denomination: 20 };
const one: Money = { denomination: 1 };
const nineteen = subtractObjects<Money>(twenty, one);
/* const nineteen: Money */
console.log(nineteen.denomination) // 1 | 5 | 10 | 20 | 50 | 100 at compile time
// but 19 at runtime

Here, the type Money has a single known property denomination whose value must be one of a particular set of numbers. And since Money extends Record<string, number>, you are allowed to pass two Moneys into subtractObjects(). And according to the call signature for subtractObjects(), that means a Money will come out. The compiler says that nineteen is valid Money. But if you check at runtime, its denomination property is not one of the allowed values. You've accidentally produced counterfeit currency.

If it's not clear from that code example why counterfeit currency is a problem, then note that once you have a situation where the compiler is mistaken about the type of a value, it can lead to runtime errors:

function moneyName(m: Money) {
  return {
    1: "One",
    5: "Five",
    10: "Ten",
    20: "Twenty",
    50: "Fifty",
    100: "One Hundred"
  }[m.denomination]
}
console.log(moneyName(one).toUpperCase()) // ONE
console.log(moneyName(twenty).toUpperCase()) // TWENTY
console.log(moneyName(nineteen).toUpperCase()) //            
  • Related