Reputation: 33
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
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
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