打字稿:

时间:2016-05-03 13:45:37

标签: casting typescript

我希望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
  }
})

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;

这就是内联版本有效的原因。在这种情况下,无法进行更新。