Historia pytania:
Operator in został odziedziczony przez TypeScript z JavaScriptu i jest szeroko stosowany do sprawdzania obecności właściwości w obiekcie. W TypeScript jego semantyka nabiera dodatkowego znaczenia z powodu systemu typów — ważne jest nie tylko posiadanie wartości, ale także jej zadeklarowanie na poziomie typu, zwłaszcza w przypadku właściwości opcjonalnych.
Problem:
Często programiści w TypeScript mylą proste sprawdzenie na undefined z sprawdzeniem obecności samej właściwości w obiekcie. Błąd może wystąpić, jeśli właściwość istnieje, ale jej wartość to undefined lub jeśli jest odziedziczona, a nie zdefiniowana bezpośrednio w obiekcie.
Rozwiązanie:
Operator in sprawdza, czy właściwość rzeczywiście jest obecna w łańcuchu prototypów obiektu, niezależnie od jej wartości. To odgrywa ważną rolę w przypadku sprawdzania właściwości opcjonalnych lub obliczanych, co pozwala uniknąć niespodziewanej obsługi wartości falsy:
Przykład kodu:
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
Kluczowe cechy:
in określa obecność właściwości w obiekcie lub jego prototypie, a nie tylko istnienie wartościJak różni się sprawdzanie na undefined (obj.prop !== undefined) od użycia in?
Sprawdzanie na undefined określa tylko wartość, ale nie obecność właściwości. Jeśli właściwość istnieje, ale jest undefined — wynik będzie true. Jeśli właściwości w ogóle nie ma, jego wartość również będzie undefined, ale semantycznie są to różne przypadki.
Przykład:
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
Czy operator in może sprawdzić obecność właściwości tylko w samym obiekcie, ignorując łańcuch prototypów?
Nie, standardowy in sprawdza również prototypy. Aby sprawdzić tylko swoje, użyj metody hasOwnProperty:
const obj = Object.create({ foo: 123 }); obj.bar = 456; console.log('foo' in obj); // true console.log(obj.hasOwnProperty('foo')); // false
Czy można za pomocą operatora in prowadzić typowe narrowing dla typów unii w TypeScript?
Tak, w TypeScript in przypomina Discriminated Union i pozwala na zawężenie typu do określonej wersji:
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number }; function getArea(shape: Shape) { if ('radius' in shape) { // Tutaj shape — koło return Math.PI * shape.radius ** 2; } // Tutaj shape — kwadrat return shape.size ** 2; }
inin dla tablic (na tablicach zwraca true dla indeksów liczbowych i odziedziczonych właściwości)in dla wartości innych niż obiekty (na przykład null lub undefined)W projekcie z tablicą użytkowników sprawdzano opcjonalne pole za pomocą user.name !== undefined, co prowadziło do nieprawidłowej logiki dla użytkowników, których właściwość była wyraźnie zadeklarowana jako undefined.
Plusy:
Minusy:
Użyto in do precyzyjnego sprawdzenia obecności opcjonalnego pola użytkownika niezależnie od jego wartości, co pozwoliło poprawnie odróżnić "brak pola" od "pole jest, ale wartość nie jest przypisana".
Plusy:
Minusy:
in, co nie zawsze jest oczywiste dla nowicjuszy