似乎打字稿允许将任何类型的类型转换为具有类型为number
键的映射。下面是一个示例代码:
type NumberMap = {
[key: number]: string
}
type AnyType = {
foo: string
bar: boolean[]
}
const anyTypeObj: AnyType = { foo: "foo", bar: [false] }
const numberMap: NumberMap = anyTypeObj
我希望最后一行在尝试将AnyType
分配给NumberMap
时给我一个类型错误,但事实并非如此。但是,将对象文本直接分配给类型为NumberMap
的变量会按预期工作,并导致类型错误:
const numberMap: NumberMap = { foo: "foo", bar: [false] }
这是打字稿操场上上述代码的链接。
为什么打字稿允许将任何类型的对象转换为NumberMap
? 类型{ [key: number]: string }
有什么特别之处让它以这种方式表现吗?
Typescript 在将对象文本分配给变量时会执行一些额外的检查。这称为多余财产检查。这个想法是,分配给类型不具有完全相同属性的变量的对象文本很可能是一个错误。另一方面,不完全匹配的重新赋值和参数值在 Javascript 中很常见,因此当右侧表达式不是对象文字时,只要值的类型与变量的类型兼容,就允许不精确的赋值。
在您的示例中,anyTypeObj
与NumberMap
兼容。您可以将anyTypeObj
视为空NumberMap
。NumberMap
协定指定,如果通过数字键访问属性,则结果将为字符串。anyTypeObj
没有数字键,因此不存在不正确的情况。
请注意,一旦将anyTypeObj
分配给numberMap
,就不能通过numberMap
访问foo
或bar
属性,因为NumberMap
类型不会声明这些属性。
编辑:玛格丽特克鲁接着问了这个问题:
为什么
{ [key: number]: string }
不强制对象上的所有键都必须是number
类型并且值string
? 因为anyTypeObj
具有具有string
键的属性,所以我希望在这种情况下NumberMap
转换失败。
我的解释是,变量可以接受与变量类型兼容类型的任何值。我认为兼容性是子类型。例如:
const a: Animal = new Cow()
如果Cow
是Animal
的子类,即使Cow
具有Animal
没有的属性,这也有效。
在面向对象的语言中,类型要么是类,要么是接口,只有当B
显式扩展或实现A
时,类型B
才被视为A
的子类型。但是有些语言(包括打字稿(采取更一般的观点。"类型"的最一般定义是对满足特定条件的一组可能值的描述。 如果B
描述一组值,而该值是A
描述的一组值的子集,则B
是A
的子类型。B
名称是否A
在其定义中并不重要。
NumberMap
描述了其数值属性都具有类型string
的所有对象的集合。
AnyType
是NumberMap
的子类型,因为其所有数值属性的类型均为string
。(这是微不足道的,因为AnyType
没有数值属性(。AnyType
上的字符串属性确实干扰了NumberMap
的合约,因为NumberMap
没有说明字符串属性。