问题历史:
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 (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; }
in 检查混淆in(在数组上对数字索引和继承属性返回 true)in在一个用户数组项目中,使用 user.name !== undefined 检查可选字段,这导致了对那些属性明确设置为 undefined 的用户的不正确逻辑。
优点:
缺点:
使用 in 进行精确检查,以确定用户的可选字段是否存在,而不管其值如何,从而正确区分“没有字段”和“字段存在但未指定值”。
优点:
缺点:
in 操作符的语义,这对新手并不总是显而易见。