我是 TypeScript 的新手。我写了一段如下代码,"它有效"(我用TypeScript 3.7编译我的代码(:
const something: { [foo: string]: string } = <...> ;
我不想专注于=
的右侧。<...>
是实际代码的占位符。
我想准确地理解:左手边的结构是什么?我们怎么称呼它?它由哪些部分组成(以及我们如何称呼这些部分(,它实际上提供了哪些保证?
我短暂地认为{ [foo: string]: string }
是一个匿名接口声明,但在网上搜索这个术语只会确认我应该更好地了解而不是思考:-(。这让我来到了这里。
我应该说出我的发现。我查找了 TS 规格。我发现{ [foo: string]: string }
可能是一个对象类型文字,其中内部部分(TypeBody
(似乎是索引签名。索引签名似乎由[ BindingIdentifier : string ] TypeAnnotation
组成。我必须说,在阅读了第3.9.4 Index Signatures
部分之后,我根本不清楚事情。特别是:BindingIdentifier
的作用是什么,foo
在我的情况下?我发现显示的代码中的foo
可以是任何东西(使用key
,x
,bar
而不是foo
编译和运行良好(,我发现这令人困惑,这是一种代码气味。我可以在没有foo
的情况下逃脱,即没有BindingIdentifier
吗?我认为最后一部分与 https://github.com/Microsoft/TypeScript/issues/7803 有关,但这次讨论并没有真正结束,已经有几年了。
其次,这是我的意图:我想用一个内联类型表达式声明变量something
,这应该意味着该值必须是完全由string
键和string
值组成的对象。根据tsc
的反馈,我认为我的尝试实现了这一目标。但事实真的如此吗,有没有更清洁的方法来实现这一目标?
它保证某些东西将是带有字符串键和字符串值(同样不能未定义(的 JavaScript 对象(并且不能未定义(。
我个人尽量保持内联文字非常谨慎,所以我通常会将其导出为单独的类型,例如
interface MySomething {
[key: string]: string;
}
此外,由于这是一个如此常见的构造,我创建了我唯一的全局类型,以便能够在任何地方本机使用它。这是一种Dict
类型,如下所示:
// src/global.d.ts
declare interface Dict<T = any> {
[key: string]: T;
}
这意味着在这种情况下,我可以简单地拥有...
const something: Dict<string> = <...>;
解决您的另一个观点,密钥的名称确实是任意的,但我发现根据它所代表的内容给它一个合理的名称是有帮助的,作为任何使用该类型的人的代码即文档的形式。