编程前端开发者

谈谈 TypeScript 中的过剩属性检查(Excess Property Checks)机制。它们如何在对象上工作,编译阶段检查了什么,以及在具有动态属性的对象类型化时可能出现的问题。

用 Hintsage AI 助手通过面试

回答。

TypeScript 实现了过剩属性检查机制(Excess Property Checking),以增强安全性,警告程序员在将对象字面量直接赋值给特定类型变量时声明中的错误。该机制的出现是因为 TypeScript 的结构类型能够允许多余的未声明属性,这常常会导致程序逻辑错误,特别是在处理 API 或表单时。

问题背景

过剩属性检查的引入旨在提高前端开发的安全性,其中对象的结构通常遵循严格的契约模型(例如,用于 JSON 的序列化)。当对象作为字面量创建并立即传递给函数或存储到特定类型的变量中时,TS 会执行 "过剩" 检查——查找在预期类型中未描述的多余属性。

问题

当对象包含拼写错误或多余属性时,程序员的错误可能不会被注意,而这样的属性不会被正确使用,甚至在业务逻辑中完全看不见。此外,过剩属性检查可能会意外触发——例如,如果对象没有明确类型化或者通过扩展运算符或中间变量处理。

解决方案

TypeScript 对被直接赋值给变量或函数参数的对象字面量应用过剩属性检查。检查查找对象的所有属性并与声明的类型进行比较——如果存在多余属性,将会出现编译错误。

interface UserProfile { name: string; age: number; } const user: UserProfile = { name: "Sam", age: 25, email: "sam@mail.com" // 错误:多余属性 email };

要绕过过剩检查,例如,对于具有动态属性或部分类型化的对象,可以使用索引签名或中间变量。

interface FlexibleUser { name: string; [prop: string]: any; // 索引签名允许任何新属性 } const user2: FlexibleUser = { name: "Sam", age: 25, email: "sam@mail.com" // 正常工作 };

主要特点:

  • 过剩属性检查仅适用于显式类型化的变量直接赋值的对象字面量。
  • 可以通过索引签名或通过中介变量赋值来绕过检查。
  • 保护对微不足道的拼写错误和数据结构错误。

设问注意事项。

如果创建一个带有多余属性的对象,将其赋值给没有类型的变量,然后再重新赋值类型,过剩属性检查会生效吗?

不会,过剩检查仅在直接赋值字面量时有效。如果对象预先创建,并在后续指定类型,则不会检测到多余属性。

const temp = { name: "John", age: 18, foo: "bar" }; const u: UserProfile = temp; // 没有错误,foo 被忽略

过剩属性检查是否适用于类和类的实例?

不,这种检查不适用于类和类的实例,仅适用于对象字面量。

是否可以在 TS 设置中全局禁用过剩检查?

不,没有单独的设置来禁用它。但是,可以为属性指定索引签名或使用类型断言('as')操作符,明确表明不需要检查。

const special: UserProfile = { name: "Max", age: 22, hobby: "js" } as UserProfile;

类型错误和反模式

  • 使用类型断言来禁用检查(可能会导致对象结构中错误的遗漏)。
  • 不合理地使用索引签名会破坏类型描述的严格性。

生活中的例子

负面案例

开发人员为用户表单创建接口,允许通过 [key: string]: any 的方式接收所有属性,以避免额外字段出现错误。

优点: 在动态数据下不会出现编译错误

缺点: 任何表单结构错误或拼写错误都不会被发现,难以查找 Bug

正面案例

开发人员定义严格的接口,并使用单独的函数将动态数据转换为严格结构,并进行预验证。

优点: 接口始终与期望的合同一致,编译器捕捉拼写错误,维护性高

缺点: 需要编写额外的代码进行验证和映射动态数据