TypeScript type in a for... in loop on an object

Viewed 943

I am using for... in loop to iterate over an object using square brackets notation, TypeScript complains saying the following:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'EVENT'. No index signature with a parameter of type 'string' was found on type 'EVENT'.ts(7053)

I Know if this wasn't a loop I could tell TypeScript that "a" can be only certain values, but by using a loop I can't give types so I don't know what to do The problem is I think I can't tell Typescript that in event[a] "a" can take a few values only

interface EVENT {
    imageURL: string;
    artist: string;
    location: string;
    city: string;
    seat: number;
    direction: string;
    country: string;
    type: string;
    date: string;
    tickets_available: number;
    tickets_left: number;
    id: string;
    description: string;
    price: number;
  }
data.filter((event: EVENT) => {
        // a = key of the object data
        for (let a in event) {
          let aSplit =
            typeof event[a] === "string"
              ? event[a].split(" ").map((element: string) => element.toLowerCase())
              : event[a];
          // b = word of searchTerm string
          for (let b of querySplit) {
            if (
              a === "artist" ||
              a === "location" ||
              a === "city" ||
              a === "country" ||
              a === "type"
            ) {
              if (aSplit.includes(b.toLowerCase())) matches.push(event);
            }
          }
        }
      });

I am using the latest typescript with the latest nextJS framework, tsconfig set to aim for ES2015 ScreenShot of the code

3 Answers

I usually use the following pattern:

interface Foo {
    str: string,
    num: number,
}

const foo: Foo = {
    str: "one",
    num: 1,
};

let i: keyof Foo;
for (i in foo) {
    console.log(foo[i]);
}



Playground Link

As the TypeScript error says, "The left-hand side of a 'for...in' statement cannot use a type annotation."

However, you can create a typed variable within the for...in loop that does have a type, and use that to index your object.

Here's a modified version of the snippet in your question. I've added an empty data variable and removed the querySplit code because those parts don't have the necessary context in your snippet to resolve TypeScript errors.

You'll also note I had to replace your event[a] code with a variable, because accessing the value using square bracket notation repeatedly doesn't work properly with TypeScript's type narrowing.

interface EVENT {
    imageURL: string;
    artist: string;
    location: string;
    city: string;
    seat: number;
    direction: string;
    country: string;
    type: string;
    date: string;
    tickets_available: number;
    tickets_left: number;
    id: string;
    description: string;
    price: number;
}

// Just creating an empty array so TypeScript doesn't complain about `data`
let data: EVENT[] = [];

data.filter((event: EVENT) => {
    // a = key of the object data
    for (let a in event) {
        // Create new typed variable for this iterations' value of `a`
        const key = a as keyof EVENT;
        // Look up `event[key]` once so TypeScript can narrow its type
        const value = event[key];

        let aSplit = typeof value === "string"
        ? value.split(" ").map((element: string) => element.toLowerCase())
        : value;

        // More stuff here
    }
});

TypeScript Playground

I don't want to be forced to use let instead of const, so I did this and it worked, it's a little more verbose, but I'm ok with it:

interface Foo {
    str: string,
    num: number,
}

const foo: Foo = {
    str: "one",
    num: 1,
};


for (const i in foo) {
    console.log(foo[i as keyof Foo]);
}

Related