为啥 TypeScript 有时只能通过字符串索引对象
Posted
技术标签:
【中文标题】为啥 TypeScript 有时只能通过字符串索引对象【英文标题】:Why can TypeScript only Sometimes Index an Object by a String为什么 TypeScript 有时只能通过字符串索引对象 【发布时间】:2020-05-14 03:23:05 【问题描述】:为什么当字符串是常量或简单字符串变量时,TypeScript 可以按字符串索引类型化对象,但如果从数组中拉出字符串,则无法通过字符串索引类型化对象
即考虑如下代码
class Foo
public bar: string = 'hello';
public test()
// this works
console.log(this['bar'])
// this also works
const index = 'bar';
console.log(this[index])
// in both cases above I have successfully used
// a string as an index for my type Foo
// However, this doesn't work
const props:string[] = ['bar']
for(const [key,value] of props.entries())
console.log(value); // prints 'bar' to terminal/console
console.log(this[value])
// Nor does this
for(let i=0;i<props.length;i++)
console.log(this[props[i]])
// when looping over an array of string and trying to use the
// string to index the object, I get the following error
// why.ts:20:25 - error TS7053: Element implicitly has an 'any'
// type because expression of type 'string' can't be used to
// index type 'Foo'.
const foo = new Foo;
foo.test()
class Foo
public bar: string = 'hello';
public test()
// this works
console.log(this['bar'])
// this also works
const index = 'bar';
console.log(this[index])
// in both cases above I have successfully used
// a string as an index for my type Foo
// However, this doesn't work
const props:string[] = ['bar']
for(const [key,value] of props.entries())
console.log(value); // prints 'bar' to terminal/console
console.log(this[value])
// Nor does this
for(let i=0;i<props.length;i++)
console.log(this[props[i]])
// when looping over an array of string and trying to use the
// string to index the object, I get the following error
// why.ts:20:25 - error TS7053: Element implicitly has an 'any'
// type because expression of type 'string' can't be used to
// index type 'Foo'.
const foo = new Foo;
foo.test()
这两种方法都有效。
console.log(this['bar'])
//...
const index = 'bar';
console.log(this[index])
TypeScript 能够通过字符串索引我的对象。
然而,后面的例子中我在一个字符串数组上循环
const props:string[] = ['bar']
for(const [key,value] of props.entries())
console.log(value); // prints 'bar' to terminal/console
console.log(this[value])
for(let i=0;i<props.length;i++)
console.log(this[props[i]])
不会运行/编译。我收到以下错误。
why.ts:42:17 - error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Foo'.
No index signature with a parameter of type 'string' was found on type 'Foo'.
42 console.log(foo[value])
所以这个错误消息——“字符串”类型的表达式不能用于索引“Foo”类型似乎与我的前两个例子背道而驰。
那么这里发生了什么?帮助一个糟糕的动态语言程序员理解 TypeScript 试图告诉我什么。一个样本的奖励积分实际上允许我迭代一个字符串数组并将一个字符串用作对象索引。
【问题讨论】:
【参考方案1】:答案很简单,如果 typescript 可以证明访问是安全的,则允许索引。
当您编写 this['bar']
时,打字稿会看到字符串文字,它可以轻松检查 this
是否具有属性 bar
当你写const index = 'bar';
时,你可能认为index
的类型是string
,但实际上不是,index
的类型是string literal type'bar'
,所以typescript会知道唯一可能的index
中的值是 'bar'
。由于index
只能保持bar,typescript可以通过检查this
有一个属性bar
来检查访问this[index]
是否有效
当您编写const props:string[]
时,打字稿不会对props
做出任何其他推断,它是string
的数组。这意味着当您访问this[prop]
时,打字稿需要确保this
可以被任何string
索引,因为它没有索引签名,所以它不是,因此访问会引发错误。如果你使用as const
来让 ts 推断数组的字面量类型而不是string
并移除显式注解,你将能够执行索引访问:
const props = ['bar'] as const
for(const [key,value] of props.entries())
console.log(value);
console.log(this[value])//ok
for(let i=0;i<props.length;i++)
console.log(this[props[i]])
Playground Link
如果您确定prop
是this
的键,您也可以使用类型断言:
const props = ['bar']
for(const [key,value] of props.entries())
console.log(this[value as keyof this])
或者,如果您想要真正花哨,您可以使用自定义类型保护或自定义类型断言,但这似乎有点过头了。
【讨论】:
放弃我的答案,只添加文档链接作为评论:OP 可能想了解string literal types 谢谢,这正是我想读的。我怀疑这是由于无法从数组中推断出字符串,但从一位明确的专家那里听到它总是让头脑放松。另外,我没有意识到你可以在 TypeScript 中使用实际的常量数组(相对于变量是 const 但不是它的常量),keyof this
现在有点让我心烦意乱(以一种好的方式)。你帮助我恢复了我充满活力的公牛 - t :)
@ALANSTORM 仅供参考,keyof
适用于任何类型,而不仅仅是 this
以上是关于为啥 TypeScript 有时只能通过字符串索引对象的主要内容,如果未能解决你的问题,请参考以下文章