编程前端开发工程师

在 TypeScript 中,如何使用 'in' 操作符检查对象中属性的存在性?它的使用与简单的 undefined 检查有什么区别?在 TypeScript 编程中何时以及为什么使用该操作符?

用 Hintsage AI 助手通过面试

答案。

问题历史

in 操作符是从 JavaScript 继承而来的,并广泛用于检查对象中属性的存在性。在 TypeScript 中,由于类型系统的原因,其语义赋予了额外的意义 — 不仅要关注值的存在,还要关注其在类型级别上的声明,尤其是在处理可选属性时。

问题

TypeScript 的开发者常常将简单的 undefined 检查与检查对象中属性的存在性混淆。当属性存在但其值为 undefined,或者属性被继承而不是在对象上直接定义时,可能会出现错误。

解决方案

in 操作符检查属性是否确实存在于对象的原型链中,无论其值是什么。这在检查可选或计算属性时尤其重要,可以避免意外处理 falsy 值:

示例代码:

interface User { id: number; name?: string; } const user1: User = { id: 1 }; const user2: User = { id: 2, name: undefined }; console.log('name' in user1); // false console.log('name' in user2); // true

关键特性:

  • in 操作符确定属性在对象或其原型中的存在性,而不仅仅是存在值。
  • 不依赖于属性的值(可以是 undefined, null,甚至 false)。
  • 在处理可选属性和带有划分的类型时特别有用。

反向问题。

检查 undefined (obj.prop !== undefined) 与使用 in 有何不同?

检查 undefined 仅确定值,而不确定属性的存在性。如果属性存在但为 undefined,结果将为 true。如果属性完全不存在,其值也将是 undefined,但在语义上这是不同的情况。

示例:

const obj: any = { foo: undefined }; console.log('foo' in obj); // true console.log(obj.foo !== undefined); // false console.log('bar' in obj); // false console.log(obj.bar !== undefined); // false

in 操作符是否可以仅检查对象本身的属性,而忽略原型链?

不,标准的 in 会检查原型。要仅检查自身的属性,请使用 hasOwnProperty 方法:

const obj = Object.create({ foo: 123 }); obj.bar = 456; console.log('foo' in obj); // true console.log(obj.hasOwnProperty('foo')); // false

是否可以通过 in 操作符对 TypeScript 中的联合类型进行类型收窄?

是的,在 TypeScript 中,in 类似于标识符联合(Discriminated Union),可以将类型收缩为特定变体:

type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number }; function getArea(shape: Shape) { if ('radius' in shape) { // 这里 shape 是圆 return Math.PI * shape.radius ** 2; } // 这里 shape 是正方形 return shape.size ** 2; }

类型错误与反模式

  • 将简单的 undefined 检查与 in 检查混淆
  • 在数组上使用 in(在数组上对数字索引和继承属性返回 true)
  • 尝试对非对象值(例如 null 或 undefined)使用 in

生活中的示例

负面案例

在一个用户数组项目中,使用 user.name !== undefined 检查可选字段,这导致了对那些属性明确设置为 undefined 的用户的不正确逻辑。

优点:

  • 快速和简单的实现

缺点:

  • 当字段的值为 undefined 时会出错
  • 如果数据模式更改,重构时会遇到问题

正面案例

使用 in 进行精确检查,以确定用户的可选字段是否存在,而不管其值如何,从而正确区分“没有字段”和“字段存在但未指定值”。

优点:

  • 正确的业务逻辑
  • 代码更易于维护,隐藏错误更少

缺点:

  • 需要了解 in 操作符的语义,这对新手并不总是显而易见。