为啥 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

如果您确定propthis 的键,您也可以使用类型断言:

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 有时只能通过字符串索引对象的主要内容,如果未能解决你的问题,请参考以下文章

为啥 TypeScript 对象不能用泛型类型索引?

#yyds干货盘点#MySQL学习-为啥有时候会选错索引

为啥有时 CNN 模型只能预测所有其他类别中的一类?

为啥 TypeScript 接受被覆盖方法的正确返回,即使它不可访问?

为啥 TypeScript 中没有重载的构造函数实现?

为啥在sql server中每张表只能创建一个聚集索引?