简体   繁体   English

Typescript 元素隐式具有类型 any 和 for...in 循环

[英]Typescript element implicitly has type any with for...in loops

I have a JSON object imported from a JSON file (with resolveJsonModule: true ).我有一个从 JSON 文件导入的 JSON 对象(使用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...in循环时,

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'我知道这意味着迭代器key的类型是string而不是类型'prop1' | 'prop2' 'prop1' | 'prop2' . '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 .但我不明白为什么迭代器没有得到这种类型,因为我明确地遍历myobject的属性名称。 Did I miss a tsconfig property that enables this behavior?我是否错过了启用此行为的 tsconfig 属性? 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.这似乎有点作弊,我觉得有更好的方法来做到这一点。

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这样,在添加属性或重命名时不会中断

Three solutions for typing for...in loops, I am aware of: for...in循环中输入for...in三种解决方案,我知道:

1. Type assertion 1. 类型断言

A type assertion will force key type to be narrowed to myobject keys: 类型断言将强制将key类型缩小到myobject键:

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

Playground 操场

2. Declare key variable explicitely 2.明确声明关键变量

The key variable cannot be typed inside the for-in loop, instead we can declare it outside: key 变量不能在 for-in 循环内输入,我们可以在外面声明它:

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

Playground 操场

3. Generics 3. 泛型

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 .for...in循环中的key默认为类型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.这是由于 TypeScript 的结构类型系统:确切的属性键形状仅在运行时知道,编译器无法静态分析,在编译时对象上存在哪些属性。 A key type narrowed to myobject properties would make the for...in loop an unsafe operation type-wise.缩小到myobject属性的key类型会使for...in循环成为不安全的操作类型。

More infos更多信息

Note: Some linked resources discuss Object.keys , for which the same argumentation holds.注意:一些链接资源讨论了Object.keys ,同样的论点也成立。

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 .for (var k in x)其中x是某种类型的T ,也只是肯定地说, k的类型是keyof T时的确切类型xT 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 .如果x的实际类型是T的子类型,正如我们的赋值兼容性规则所允许的那样,您将看到k中不是keyof T类型的keyof T

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}`)
     });
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM