Alvaro
Alvaro

Reputation: 41605

Typescript error when changing property of object dynamically

I'm having trouble when I try to update an Object's property based on a dynamic variable. I've checked a few more answers on stackoverflow but couldn't manage to find a solution.

export interface Bikes {
  totals: Array<number>;
}

interface Products {
  cars:  Cars;
  bikes:  Bikes;
}

export interface RawData {
    products: Products
}

demo( data: RawData ) {
  // data.products contains both "cars" and "bikes" properties.
  for (var type in data.products) { 
    for (var product in data.products[type].totals) {// <-- error here
                        -------------
     ....
   }
}

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

I've also tried using:

export interface RawData {
    products: keyof Products
}

And then the error appears on data.products[type].totals

Property 'bikes' does not exist on type 'string'.

Upvotes: 1

Views: 120

Answers (1)

In your case, variable type is infered to string.

TS disallows you to use string type as index of RawData['products'] type.

If you want to assure TS that you can do it, you have two ways:

1) Make type assertion

  function demo(data: RawData) {
    for (const type in data.products) {
       // explicitly define type for `type` varable
      for (const product in data.products[type as keyof RawData['products']]) { // ok

      }
    }
  }

2) Make Products indexable

  interface Products {
    cars: Cars;
    bikes: Bikes;
    [prop: string]: Cars | Bikes
  }

UPDATE

  interface Bikes {
    totals: Array<number>;
  }

  type Mix = { foo: 'a' };

  type Products = {
    cars: Cars;
    bikes: Bikes;
    mix?: Mix;
  }
  type Values<T> = T[keyof T]

  type MakeIndexed<T> = T & { [prop: string]: Values<T> }


  interface RawData {
    products: MakeIndexed<Products>
  }

  function demo(data: RawData) {
    for (const type in data.products) {
      for (const product in data.products[type]) { // ok

      }
    }
  }

Upvotes: 2

Related Questions