8

I am trying to use reduce with Typescript to reach a total count of incoming messages. I'm confused on how to add an index signature. I keep getting the error: " Element implicitly has an 'any' type because type '{}' has no index signature." on variables newArray and counts[k]. I have read through several similar questions but have not figured out how to apply them to my specific scenario. I am new to JavaScript and TypeScript.

Here is my array:

        var data = [
        { time: '9:54' }, 
        { time: '9:54' },
        { time: '9:54' },
        { time: '9:55' }, 
        { time: '9:55' },
        { time: '9:55' },
        { time: '9:55' },
        { time: '9:56' }, 
        { time: '9:56' },
        { time: '9:56' },
        { time: '9:56' },
        { time: '9:57' }, 
        { time: '9:57' },
        { time: '9:57' },
        { time: '9:57' },
        { time: '9:57' }
    ];

This is how I need my array for use in a rechart graph:

        var dataWithCounts = [
        { time: '9:54', messages: 3 }, 
        { time: '9:55', messages: 4 }, 
        { time: '9:56', messages: 4 }, 
        { time: '9:57', messages: 5 }
    ];

With the help of 'Just a Student's' answer from this Stack Overflow question: Group and count values in an array , I have the following.

        var counts = data.reduce((newArray, item) => {
           let time = item.time;
           if (!newArray.hasOwnProperty(time)) {
               newArray[time] = 0;
           } 
           newArray[time]++;
           return newArray;
        }, {});

        console.log(counts);

        var countsExtended = Object.keys(counts).map(k => {
           return { time: k, messages: counts[k] };
       });

       console.log(countsExtended);

Where and how do I declare an index signature? Below are various things I've tried.

  • let newArray: { [time: string] }; and receive a duplicate identifier error.

  • adding string to the parameter var counts = data.reduce((newA:string, item) gives me an error "Element implicitly has an 'any' type because index expression is not of type 'number'."

  • adding newA[time].toString()gives me errors, "The left-hand side of an assignment expression must be a variable or a property access."

Bethany
  • 653
  • 4
  • 9
  • 16
  • Not a typescript guy but would guess that it's because you don't declare the third and fourth arguments to the `reduce` callback. The function you pass to `reduce` gets 1. The accumulator. 2. The current item in the array. 3. The current index. 4. The original array. – Jared Smith Feb 13 '18 at 18:20
  • Set `noImplicitAny` to false in your tsconfig.json file. – HaveSpacesuit Feb 13 '18 at 18:22

4 Answers4

13

The type of your accumulator in the .reduce call is almost certainly the issue. Since it is just given as {}, that's what its type is inferred as, and the type {} doesn't have an index signature. You can fix this by casting your initial value so that it does include a signature:

var counts = data.reduce((newArray, item) => {
    let time = item.time;
    if (!newArray.hasOwnProperty(time)) {
        newArray[time] = 0;
    } 
    newArray[time]++;
    return newArray;
}, {} as {[key: string]: any}); // <-- note the cast here

I gave the index signature type any, but you may want to make it something more specific for your particular case.

CRice
  • 22,449
  • 3
  • 48
  • 54
4

The correct types for your array would be:

Array<{time: string}>

or:

{time: string}[]

or:

{[key: number]: {time: string}}
Jonas Wilms
  • 106,571
  • 13
  • 98
  • 120
0

If I'm understanding you, you need to create the interface for the object...

interface counts_t{
    time?: string,
    messages?: number
}

var data: counts_t[] = [
    { time: '9:54' }, 
    { time: '9:55' }, 
    { time: '9:56' }, 
    { time: '9:57' }
];

    var counts = data.reduce((newArray:counts_t, item:counts_t) => {
       let time = item.time;
       if (!newArray.hasOwnProperty(time)) {
           newArray[time] = 0;
       } 
       newArray[time]++;
       return newArray;
    }, {});

    console.log(counts);

    var countsExtended: counts_t[] = Object.keys(counts).map(k => {
       return { time: k, messages: counts[k] };
    });

   console.log(countsExtended);
I wrestled a bear once.
  • 19,489
  • 16
  • 63
  • 110
  • Thanks 'I wrestled a bear once'. That may be another way to solve it. CRice's answer above worked. Awesome username btw – Bethany Feb 13 '18 at 18:58
0

You have to add a type that has an index signature. Ex.

const myObj: {[key: string]: string} = {
  someProperty: 'someValue',
};

The {[key: string]: any} is telling Typescript that this obj has an index signature of type string.

charri
  • 456
  • 2
  • 5