1

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'.

2
  • What are you trying to achieve with the for-in loop? And what is the definition of the Cars and Bikes types? Commented Apr 22, 2021 at 16:32
  • I've updated the question. It loops through the different product types coming from the JSON data. (bikes and cars) Commented Apr 22, 2021 at 16:37

1 Answer 1

2

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

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

3 Comments

If I have an optional property inside the Products interface, such as mix:? Mix then adding your last line ([prop: string]: Cars | Bikes) shows me this error Property 'mix' of type 'Cars | Bikes | undefined' is not assignable to string index type 'Cars | Bikes'.ts
Cannot find name 'Values'.ts(2304) It seems Values is not defined on your example.
You should not look for Values.ts. In my example, Values - is just utility type

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.