任何类型都可以在 Typescript 中转换为数字映射



似乎打字稿允许将任何类型的类型转换为具有类型为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 中很常见,因此当右侧表达式不是对象文字时,只要值的类型与变量的类型兼容,就允许不精确的赋值。

在您的示例中,anyTypeObjNumberMap兼容。您可以将anyTypeObj视为空NumberMapNumberMap协定指定,如果通过数字键访问属性,则结果将为字符串。anyTypeObj没有数字键,因此不存在不正确的情况。

请注意,一旦将anyTypeObj分配给numberMap,就不能通过numberMap访问foobar属性,因为NumberMap类型不会声明这些属性。

编辑:玛格丽特克鲁接着问了这个问题:

为什么{ [key: number]: string }不强制对象上的所有键都必须是number类型并且值string? 因为anyTypeObj具有具有string键的属性,所以我希望在这种情况下NumberMap转换失败。

我的解释是,变量可以接受与变量类型兼容类型的任何值。我认为兼容性是子类型。例如:

const a: Animal = new Cow()

如果CowAnimal的子类,即使Cow具有Animal没有的属性,这也有效。

在面向对象的语言中,类型要么是类,要么是接口,只有当B显式扩展或实现A时,类型B才被视为A的子类型。但是有些语言(包括打字稿(采取更一般的观点。"类型"的最一般定义是对满足特定条件的一组可能值的描述。 如果B描述一组值,而该值是A描述的一组值的子集,则BA的子类型。B名称是否A在其定义中并不重要。

NumberMap描述了其数值属性都具有类型string的所有对象的集合。

AnyTypeNumberMap的子类型,因为其所有数值属性的类型均为string。(这是微不足道的,因为AnyType没有数值属性(。AnyType上的字符串属性确实干扰了NumberMap的合约,因为NumberMap没有说明字符串属性。

最新更新