Typescript
let obj = { a: "hey", b: "you", c: "guys" };
for (const k in obj) {
console.log(obj[k].toUpperCase()); // error!
/* Element implicitly has an 'any' type because expression of type
'string' can't be used to index type '{ a: string; b: string; c: string; }'. */
}
这里的问题是 TypeScript 中的对象类型是开放的或可扩展的,而不是封闭的或精确的。您可以在 TypeScript 中为对象添加额外的属性,而不会违反其类型。这在某些情况下非常有用:它允许您通过添加属性来扩展接口和子类。但在其他情况下,这是一种痛苦:
编译器将 obj 视为 {a: string, b: string, c: string} 类型的值。由于这种类型并不精确,当你执行 for (const k in obj) 时,编译器知道 k 将采用值“a”、“b”和“c”,但它不知道这些是唯一可能的值:
让 hmm = { a: "hey", b: "you", c: "guys", d: 12345 };
object= hmm ; // no error!
因为值 hmm 匹配 {a: string, b: string, c: string}。 d 属性不违反类型。因此,对于所有编译器都知道的,for (const k in obj) 将枚举“a”、“b”、“c”,谁知道还有哪些其他字符串值。因此,k 的类型不是 keyof typeof obj。它只是字符串。
使用 string 类型的键索引 {a: string, b: string, c: string} 是不安全的。实际上,将 hmm 分配给 obj,调用 (12345).toUpperCase() 时会出现运行时错误。所以编译器警告你是正确的。
有关此问题答案的“官方”版本,请参阅 microsoft/TypeScript#35847。另请参阅为什么 Object.keys() 在 TypeScript 中不返回 keyof 类型?,这是相同的问题,除了使用 Object.keys(obj).forEach(k => …) 而不是 for (const k in obj ) {…}… 它有相同的答案。
如果引入了确切的类型,那么这个问题可能就会消失。对此有一个功能请求:microsoft/TypeScript#12936。但看起来这不会很快发生。
for (k in obj) {
console.log(obj[k as keyof typeof obj].toUpperCase()); // okay
}