История вопроса:
Оператор in был унаследован TypeScript от 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, но semantically это разные случаи.
Пример:
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 проводить type narrowing для union типов в 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; }
inin для массива (на массивах возвращает true для числовых индексов и унаследованных свойств)in для значений, отличных от объектов (например, null или undefined)В проекте с массивом пользователей проверяли опциональное поле с помощью user.name !== undefined, что приводило к некорректной логике для пользователей, у которых свойство было явно задано как undefined.
Плюсы:
Минусы:
Использовали in для точной проверки наличия опционального поля пользователя независимо от его значения, что позволило корректно различать "нет поля" и "поле есть, но значение не задано".
Плюсы:
Минусы:
in, что не всегда очевидно для новичков