看来,打字稿允许将任何类型的键强制转换为键类型为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] }
这里是link,上面的代码在打字稿游乐场上显示。
为什么打字稿允许将任何类型的对象强制转换为NumberMap
?
类型{ [key: number]: string }
有什么特别之处可以使其表现为这种方式?
答案 0 :(得分:3)
当将对象文字分配给变量时,Typescript会做一些额外的检查。这称为excess property checking。想法是,将对象文字分配给类型不完全相同的变量的变量很可能是错误的。另一方面,不完全匹配的重分配和参数值在Javascript中很常见,因此,如果右侧表达式不是对象文字,只要值的类型与变量的类型兼容,就允许不精确的分配。
在您的示例中,anyTypeObj
与NumberMap
兼容。您可以将anyTypeObj
视为空的NumberMap
。 NumberMap
的合同规定,如果您通过数字键访问属性,则结果将为字符串。 anyTypeObj
没有数字键,因此在任何情况下都是不正确的。
请注意,将anyTypeObj
分配给numberMap
后,您将无法再通过foo
访问bar
或numberMap
属性,因为{ {1}}类型不会声明这些属性。
编辑:margaretkru提出了以下问题:
为什么
NumberMap
并没有强制要求对象上的所有键都必须是{ [key: number]: string }
类型和值number
?因为string
具有带有anyTypeObj
键的属性,所以在这种情况下,我期望转换为string
会失败。
我的解释是,变量可以接受类型为compatible且类型为变量的任何值。我从子类型的角度考虑兼容性。例如:
NumberMap
即使const a: Animal = new Cow()
是Cow
的子类,即使Animal
是Cow
的子类也可以。
在面向对象的语言中,类型要么是类,要么是接口,并且如果Animal
显式扩展或实现B
,则类型A
仅被视为B
的子类型。但是某些语言(包括Typescript)具有更一般的看法。 “类型”的最一般定义是对满足某些条件的一组可能值的描述。如果A
描述的值集是B
描述的值的子集,则A
是B
的子类型。 A
是否在其定义中命名B
无关紧要。
A
描述了其数字属性都具有类型NumberMap
的所有对象的集合。
string
是AnyType
的子类型,因为其所有数字属性都具有类型NumberMap
。 (这是微不足道的,因为string
没有数字属性)。 AnyType
上的字符串属性确实会干扰AnyType
的合同,因为NumberMap
并没有说明字符串属性。