History of the Question:
The in operator has been inherited by TypeScript from JavaScript and is widely used for checking the presence of a property in an object. In TypeScript, its semantics take on additional significance due to the type system — it is important not only for the existence of a value but also for its declaration at the type level, especially in cases with optional properties.
Problem:
Developers in TypeScript often confuse a simple undefined check with checking the actual presence of a property in an object. An error may occur if a property exists but its value is undefined, or if it is inherited rather than directly defined on the object.
Solution:
The in operator checks whether a property truly resides in the object's prototype chain regardless of its value. This plays a key role when checking optional or computed properties, thus avoiding unexpected handling of falsy values:
Example code:
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
Key features:
in operator determines the presence of a property in the object or its prototype, not just the existence of a valueHow does checking for undefined (obj.prop !== undefined) differ from using in?
A check for undefined defines only the value, not the presence of the property. If a property exists but it is undefined, the result will be true. If the property does not exist at all, its value will also be undefined, but semantically these are different cases.
Example:
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
Can the in operator check for the presence of a property only on the object itself, ignoring the prototype chain?
No, the standard in checks the prototypes as well. To check only its own properties, use the hasOwnProperty method:
const obj = Object.create({ foo: 123 }); obj.bar = 456; console.log('foo' in obj); // true console.log(obj.hasOwnProperty('foo')); // false
Can the in operator be used for type narrowing for union types in TypeScript?
Yes, in TypeScript, in resembles Discriminated Union and allows narrowing the type to a specific variant:
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number }; function getArea(shape: Shape) { if ('radius' in shape) { // Here shape is a circle return Math.PI * shape.radius ** 2; } // Here shape is a square return shape.size ** 2; }
in checkin for arrays (it returns true for numeric indices and inherited properties)in for values other than objects (e.g., null or undefined)In a project with a user array, checked an optional field using user.name !== undefined, which led to incorrect logic for users whose property was explicitly set as undefined.
Pros:
Cons:
Used in for an accurate check of an optional user field's presence irrespective of its value, which allowed for correctly distinguishing "no field" and "field exists but value is not set".
Pros:
Cons:
in operator's semantics, which may not be clear to beginners