Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

typescript

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.

like image 431
Mustafa E. Avatar asked Jan 20 '26 14:01

Mustafa E.


2 Answers

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.

like image 140
cwharris Avatar answered Jan 22 '26 02:01

cwharris


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;
}
like image 42
Explosion Pills Avatar answered Jan 22 '26 04:01

Explosion Pills