Typescript element implicitly has type any with for…in loops

Deadly 提交于 2021-01-20 12:12:08

问题


I have a JSON object imported from a JSON file (with resolveJsonModule: true). The object looks like this:

"myobject": {
  "prop1": "foo",
  "prop2": "bar"
}

and it's type therefore looks like this:

myobject: { prop1: string, prop2: string }

That's very nice but when I try to use a for...in loop,

for (const key in myobject)  {
  console.log(myobject[key])
}

I get this error:

TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ "prop1": string; "prop2": string; }'.
  No index signature with a parameter of type 'string' was found on type '{ "prop1": string; "prop2": string; }'.

I understand that this means the iterator key is of type string and not of type 'prop1' | 'prop2'. But I don't understand why the iterator doesn't get this type because I'm explicitly iterating through the property names of myobject. Did I miss a tsconfig property that enables this behavior? I would like not to do this:

for (const key in myobject)  {
  console.log(myobject[key as 'prop1' | 'prop2'])
}

Because:

  1. I might add new properties in the future; and
  2. this seems a bit cheaty, and I feel like there is a better way to do that.

回答1:


A better way to this is:

for (const key in myobject)  {
  console.log(myobject[key as keyof typeof myobject])
}

In this way, it won't break when you add a property or rename it




回答2:


if you want to have an object to be dynamic in the future create a model like this

interface PropertyItemModel {
  propName: string;
  propValue: string;
}

and in the component you can fetch data by loop

export class AppComponent {

  items: PropertyItemModel[] = [];

  constructor() {

    this.items = [
      { propName: "1", propValue: "foo" },
      { propName: "2", propValue: "bar" }]

     this.items.forEach(item => {
        console.log(`name: ${item.propName} - value: ${item.propValue}`)
     });
  }
}



回答3:


Three solutions for typing for...in loops, I am aware of:

1. Type assertion

A type assertion will force key type to be narrowed to myobject keys:

for (const key in myobject)  {
  console.log(myobject[key as keyof typeof myobject])
}

Playground

2. Declare key variable explicitely

The key variable cannot be typed inside the for-in loop, instead we can declare it outside:

let key: keyof typeof myobject // add this declaration
for (key in myobject)  {
  console.log(myobject[key]) // works
}

Playground

3. Generics

function foo<T>(t: T) {
  for (const k in t) {
    console.log(t[k]) // works
  }
}

foo(myobject)

Playground

Why is this necessary?

key in a for...in loop will by design default to type string. This is due to the structural type system of TypeScript: the exact properties' keys shape is only known at run-time, the compiler cannot statically analyze, what properties are present on the object at compile-time. A key type narrowed to myobject properties would make the for...in loop an unsafe operation type-wise.

More infos

Note: Some linked resources discuss Object.keys, for which the same argumentation holds.

  • Why doesn't Object.keys return a keyof type in TypeScript? - by Ryan Cavanaugh

  • Comment by Anders Hejlsberg in TypeScript#12253 - also mentions for...in

  • TypeScript#32321 links to a multitude of duplicate issues

  • Specific comment towards for...in by Anders Hejlsberg:

I have my doubts about this one. In for (var k in x) where x is of some type T, it is only safe to say that k is of type keyof T when the exact type of x is T. If the actual type of x is a subtype of T, as is permitted by our assignment compatibility rules, you will see values in k that are not of type keyof T.



来源:https://stackoverflow.com/questions/59233965/typescript-element-implicitly-has-type-any-with-for-in-loops

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!