我希望MyInterface.dic
像字典name: value
,我将其定义如下:
interface MyInterface {
dic: { [name: string]: number }
}
现在我创建一个等待我的类型的函数:
function foo(a: MyInterface) {
...
}
输入:
let o = {
dic: {
'a': 3,
'b': 5
}
}
我期待foo(o)
正确,但编译器正在崩溃:
foo(o) // Typescript error: Index signature is missing in type { 'a': number, 'b': number }
我知道有一种可能的投射:let o: MyInterface = { ... }
可以解决问题,但问题是,为什么打字稿不能识别我的类型?
额外:如果内联声明o
,则工作正常:
foo({
dic: {
'a': 3,
'b': 5
}
})
答案 0 :(得分:27)
问题是当推断出类型时,o
的类型是:
{ dic: { a: number, b: number } }
这与{ dic: { [name: string]: number } }
不同。重要的是,使用顶级签名,您不允许执行o.dic['x'] = 1
之类的操作。有了第二个签名。
它们在运行时是等效的类型(实际上,它们是完全相同的值),但TypeScript安全的很大一部分来自于它们不相同的事实,并且它只允许你将对象视为字典,如果它知道它明确意图为一个。这就是阻止你不小心在对象上读写完全不存在的属性的原因。
解决方案是确保TypeScript知道它打算作为字典。这意味着:
明确地提供某个类型,告诉它它是一个字典:
let o: MyInterface
断言它是字典内联:
let o = { dic: <{ [name: string]: number }> { 'a': 1, 'b': 2 } }
确保它是TypeScript为您推断的初始类型:
foo({ dic: { 'a': 1, 'b': 2 } })
如果有一种情况是TypeScript认为它是一个只有两个属性的普通对象,然后你尝试以后再用它作为字典,那么它就会不高兴。
答案 1 :(得分:17)
TS 要我们定义索引的类型。例如,告诉编译器您可以使用任何字符串索引对象,例如myObj['anyString']
,更改:
interface myInterface {
myVal: string;
}
到:
interface myInterface {
[key: string]: string;
myVal: string;
}
您现在可以在任何字符串索引上存储任何字符串值:
x['myVal'] = 'hello world'
x['any other string'] = 'any other string'
答案 2 :(得分:9)
对我来说错误是通过使用类型而不是接口来解决的
当函数 foo
具有类型而不是用于键入参数的接口时,可能会发生此错误
type MyType {
dic: { [name: string]: number }
}
function foo(a: MyType) {}
但是传递的值是用类似接口输入的
interface MyInterface {
dic: { [name: string]: number }
}
const o: MyInterface = {
dic: {
'a': 3,
'b': 5
}
}
foo(o) // type error here
我刚用过
const o: MyType = {
dic: {
'a': 3,
'b': 5
}
}
foo(o) // it works
答案 3 :(得分:2)
您可以通过执行 foo({...o})
来解决此问题
playground
答案 4 :(得分:2)
此错误合法。你应该写这个来将类型标记为不可变:
interface MyInterface {
dic: { [name: string]: number }
}
function foo(a: MyInterface) {
...
}
const o = Object.freeze({
dic: {
'a': 3,
'b': 5
}
})
为什么?
TS 编译器不能假设 o
在初始化和调用 foo(o)
之间不会改变。
也许在您的代码中的某处写了类似下面的代码段:
delete o.dic.a;
这就是内联版本有效的原因。在这种情况下,无法进行更新。