TypeScript 中的类型兼容性基于结构子类型。 结构类型是一种仅基于其成员关联类型的方法。这与 nominal typing 相反。考虑以下代码:
interface Pet { name: string; } class Dog { name: string; } let pet: Pet; // OK, because of structural typing pet = new Dog();
如果是 Java 或者 ABAP 编程语言,上述代码会出现编译错误,因为 Pet 类型 和 Dog 类型没有任何关联,虽然其正好都有一个类型为 string 的 name 字段。
在 C# 或 Java 等名义类型的语言中,上述代码将是错误的,因为 Dog 类没有明确地将自己描述为 Pet 接口的实现者。
TypeScript 的结构类型系统是根据 JavaScript 代码的典型编写方式设计的。 因为 JavaScript 广泛使用匿名对象,如函数表达式和对象字面量,所以用结构类型系统而不是名义类型系统来表示 JavaScript 库中发现的各种关系要自然得多。
什么是健全性 Soundness
TypeScript 的类型系统允许某些在编译时无法知道的操作是安全的。 当一个类型系统具有这个属性时,它就被认为不是“健全的”。 仔细考虑了 TypeScript 允许不良行为的地方,在整个文档中,我们将解释这些地方发生的地方以及它们背后的激励场景。
TypeScript 结构类型系统的基本规则是,如果 y 至少具有与 x 相同的成员,则 x 与 y 兼容。 例如,考虑以下代码,其中包含一个名为 Pet 的接口,该接口具有 name 属性。这段代码是合法的:
interface Pet { name: string; } let pet: Pet; // dog's inferred type is { name: string; owner: string; } let dog = { name: "Lassie", owner: "Rudd Weatherwax" }; pet = dog;
为了检查 dog 是否可以分配给 pet,编译器会检查 pet 的每个属性,以在 dog 中找到对应的兼容属性。 在这种情况下, dog 必须有一个名为 name 的成员,它是一个字符串。 确实如此,因此允许分配。
同理,下列代码也合法:
interface Pet { name: string; } let dog = { name: "Lassie", owner: "Rudd Weatherwax" }; function greet(pet: Pet) { console.log("Hello, " + pet.name); } greet(dog); // OK