0

this is my Interface file :

export interface ListCount {
  centre?: string;
  cause?: string;
  totalTime?: number;
}

I am trying to make reduce to array of objects with pipe transform :

export class SumPipe implements PipeTransform {
  transform(items: ListCount[], attr: string): number {
    return items.reduce((a, b) => a + b[attr], 0);
  }
}

and in compent HTML I looking to make sum of totalTime

{{ (delayCount$ | async) | sum:'totalTime'}}

But I have this error :

error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ListCount'.

Then I change the param attr: string to attr: keyof ListCount and still have this error :

error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number'.

and

error TS2532: Object is possibly 'undefined'.

Any help please

7
  • Why not attr is number like transform(items: ListCount[], attr: number): number { Commented Mar 26, 2021 at 12:43
  • @er-sho: Because it's the property name whose values is to be summed up. Commented Mar 26, 2021 at 12:44
  • What is the purpose of passing a totalTime when it's the only type (number) that could be summed up in the array? Could there be any other properties of type number later? Commented Mar 26, 2021 at 12:45
  • @er-sho: The value contained in the property totalTime is of type number. But in context of trying to access the property using obj['totalTime'], totalTime is a string. Commented Mar 26, 2021 at 12:46
  • try reduce((a:number, b:any)=>....) Commented Mar 26, 2021 at 12:48

3 Answers 3

1

I would suggest the following:

  • restrict attr to point to numeric properties
  • take into account that properties can be optional. Use zero for those.
  • alternatively filter out missing (undefined) values.
export interface ListCount {
  centre?: string;
  cause?: string;
  totalTime?: number;
  otherTime?: number;
}

type NumericKeys<T> = {
    [P in keyof T]: T[P] extends number ? P : never;
}[keyof T];


type NumericListCountKey = NumericKeys<Required<ListCount>>;

class SumPipe  {
  transform(items: ListCount[], attr: NumericListCountKey): number {
    const elemes = items.map(elem => elem[attr]);
    return elemes.reduce((prev: number, currentVal) => prev + (currentVal ? currentVal : 0), 0);
  }
}


// alternatively, with filter
class SumPipe2  {
  static isNumber(n: number | undefined): n is number {
    return n != null;
  } 

  transform(items: ListCount[], attr: NumericListCountKey): number {
    const elems = items.map(elem => elem[attr])
                       .filter(SumPipe2.isNumber);
    return elems.reduce((prev: number, currentVal) => prev + currentVal, 0);
  }
}

Playground link

Sign up to request clarification or add additional context in comments.

Comments

0

Ok, so your code compiles without issues and works. Here is a working stackblitz. If it fails on your local environment, perhaps try checking TS version, try restarting the Angular CLI, look for other issues or at least provided information on which line exactly the error is thrown.

There are a few issues with your code though.

  • As others have noted, passing any attr to the pipe and using the bracket notation to get the property value makes no sense. Only object accepted by the pipe is ListCount[], so it HAVE to be totalTime that gets summed as it's the only numeric property. It would make sense if you made more generic pipe that would accept any[] though.

  • Your pipe is not guarded for your use-case. totalTime is an optional property, meaning that if any item doesn't have totalTime defined (which it can, according to your interface) the pipe will return NaN. Not sure if that's the desired behavior.

Comments

0

According to this article, reduce() has an optional index, which points to the current index in the array. Knowing this, I'd opt for the following solution, leaving out attr entirely.

return items.reduce((a, b, index) => a + b[index].totalTime, 0);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.