对象类型通常是开放和可扩展的,并且允许额外的属性...
TypeScript 中的对象类型通常是开放且可扩展的。一个对象是一个Person 当且仅当它有一个string 类型的name 属性。这样的对象可能会有额外的属性,但它仍然是Person。这非常有用,并且允许接口扩展形成类型层次结构:
interface AgedPerson extends Person {
age: number;
}
const agedPerson: AgedPerson = { name: "Alice", age: 35 };
const stillAPerson: Person = agedPerson; // okay
而且因为 TypeScript 有一个structural type system,所以实际上你不必为AgedPerson 声明一个接口,它就可以被视为Person 的子类型:
const undeclaredButStillAgedPerson = { name: "Bob", age: 40 };
const andStillAPersonToo: Person = undeclaredButStillAgedPerson; // okay
这里undeclaredButStillAgedPerson 的类型为{name: string, age: number},相当于AgedPerson,随后分配给Person 的工作原理相同。
尽管开放/可扩展类型很有用,但它可能会造成混淆,有时甚至是不受欢迎的。 microsoft/TypeScript#12936 有一个长期开放的请求,要求 TypeScript 支持所谓的 exact 类型,其中像 Exact<Person> 这样的东西只允许拥有name财产,没有别的。 AgedPerson 将是 Person,但不是 Exact<Person>。目前没有直接支持此类确切类型。
...但是对象字面量确实会进行过多的属性检查。
跳回:TypeScript 中的对象类型通常是开放的。但是在一种情况下,对象将被视为其类型是精确的。这是当您将 object literal 分配给变量或将其作为参数传递时。
对象文字在首次分配给变量或作为函数参数传递时会得到特殊处理并经过excess property checking。如果对象字面量具有未知类型中不存在的属性,则会出现错误。像这样:
let person: Person = { name: 'Jack', id: 209 }; // error!
// -------------------------------> ~~~~~~~
// Object literal may only specify known properties,
// and 'id' does not exist in type 'Person'.
即使{name: "Jack", id: 209} 是按照原始定义的Person,它不是Exact<Person>,所以我们得到一个错误。请注意,该错误特别提到了“对象文字”。
与以下对比,没有错误:
const samePerson = { name: 'Jack', id: 209 }; // okay
person = samePerson; // okay
将对象字面量分配给samePerson 没有错误,因为samePerson 的类型推断属于该类型
/* const samePerson: {
name: string;
id: number;
} */
那里没有多余的财产。后续将samePerson 赋值给person 也成功,因为samePerson 不是对象字面量,因此多余的属性检查不适用。
Playground link to code