Mustafa E.
Mustafa E.

Reputation: 33

Operator '+' cannot be applied to types 'number' and 'T[K]'

I have this code block:

function sumOnValue<T, K extends keyof T>(collection: Array<T>, key: K): number {
  let result = 0;
  for (const o of collection) {
    const sample = o[key];
    if (typeof o[key] === 'number') {
      result = result + sample; // [ts] Operator '+' cannot be applied to types 'number' and 'T[K]'.  red squigly line under sample.
    }
  }
  return result;
}

const sampleCharges: Array<ICharge> = [
  {
    AllowedAmount: 10,
  },
  {
    AllowedAmount: 15,
  },
  {
    AllowedAmount: 9,
  },
];

const resultedSum = sumOnValue(sampleCharges, 'AllowedAmount');
console.log(resultedSum);

As depicted in the above, I get the error from the compiler (vscode reports it as a problem) but when running the code it's fine and doesn't complain. What should I be doing here to suppress the error or to write this method 'safely via type checking'?

The objective in a nutshell is: Add up a given attribute within an array of T provided that the given attribute is a type within T and that the attribute is a number.

Upvotes: 3

Views: 4584

Answers (2)

cwharris
cwharris

Reputation: 18125

In TypeScript 2.1+ this can be done using indexed access types. Indexed access types are compile-time checks and never make it in to your JavaScript, unlike type guards.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

function pickAndReduce<T>(
    collection: Array<{ [k in keyof T]: number }>,
    key: keyof T
): number
{
    let result = 0;
    for (const o of collection) {
        result = result + o[key];
    }
    return result;
}

And the loop could be reduced, if so desired.

function pickAndReduce<T>(
    collection: Array<{ [k in keyof T]: number }>,
    key: keyof T
): number
{
    return collection.reduce((total, item) => total + item[key], 0);
}

EDIT:

Originally I had key: string, as in the example, but as pointed out in a comment, this overlooks type checking on the index access. I've change it to key: Key of T so the function may only be invoked with keys present on the given object.

Upvotes: 1

Explosion Pills
Explosion Pills

Reputation: 191779

You can use a type guard to do this -- implement a function isNumber that does the same typeof check:

function isNumber(x): x is number {
    return typeof x === "number";
}

function sumOnValue<T, K extends keyof T>(collection: Array<T>, key: K): number {
  let result = 0;
  for (const o of collection) {
    const sample = o[key];
    // use here
    if (isNumber(sample)) {
      result = result + sample;
    }
  }
  return result;
}

Upvotes: 1

Related Questions