programowanieProgramista Frontend

Jak działają modyfikatory dostępu (public, private, protected) w TypeScript? Jakie są niuanse ich użycia z dziedziczeniem i podczas transpilecji kodu do JavaScript?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W TypeScript modyfikatory dostępu używane są do ograniczania zakresu widoczności właściwości i metod w klasach:

  • public — właściwość lub metoda są dostępne wszędzie (domyślnie).
  • private — dostępne tylko w tej klasie, w której zostały zadeklarowane.
  • protected — dostępne w klasie i jej potomkach.
class Animal { public name: string; private age: number; protected kind: string; constructor(name: string, age: number, kind: string) { this.name = name; this.age = age; this.kind = kind; } } class Dog extends Animal { bark() { console.log(this.kind); // OK: protected // console.log(this.age); // Błąd: private nie widoczne w potomku } } const dog = new Dog('Szarik', 5, 'ssak'); console.log(dog.name); // OK // console.log(dog.kind); // Błąd: protected // console.log(dog.age); // Błąd: private

Niuanse:

  • W skompilowanym JavaScript modyfikatory private i protected realizowane są tylko na etapie kompilacji, fizycznej izolacji w czasie działania nie ma! Kod JS będzie zawierał wszystkie właściwości (można je na przykład uzyskać przez obejście obiektu).
  • W nowych wersjach (od TypeScript 3.8) wprowadzono składnię dla prawdziwych prywatnych pól: #field, ale to już specyfikacja JS i jest kompilowana inaczej.

Pytanie z podchwytliwą pułapką

Pytanie: Czy można uzyskać dostęp do prywatnej właściwości klasy TypeScript po kompilacji do JavaScript?

Odpowiedź: Tak, ponieważ private (i protected) — to sprawdzenie TypeScript na etapie kompilacji, po kompilacji do ES5 lub ES6 prywatność nie jest zachowana, właściwości pozostają w obiekcie i są dostępne poprzez odwołanie się do nazwy właściwości (na przykład poprzez object['privateProp']).

// Kod JS po kompilacji function Animal(name, age, kind) { this.name = name; this.age = age; this.kind = kind; } var dog = new Animal('Szarik', 5, 'ssak'); console.log(dog['age']); // 5 — dostęp jest tylko na poziomie TS!

Przykłady rzeczywistych błędów z powodu nieznajomości niuansów tematu.


Historia

W dużym projekcie programista polegał na fakcie niedostępności prywatnych pól w czasie działania. W wyniku przesłania obiektu za pomocą serializacji (JSON.stringify) do logów przypadkowo trafiły poufne dane, ponieważ typizacja TS nie chroniła przed rzeczywistym dostępem do pól.


Historia

W projekcie wdrożono mechanizm "rozszerzania" instancji klas poprzez dynamiczne dodawanie właściwości. Dynamicznie dodane właściwości nadpisywały prywatne nazwy, a ich zmiana przypadkowo została dokonana przez zewnętrzny kod. Błąd został wykryty dopiero na produkcji, a prywatność nie była zapewniona.


Historia

Podczas migracji z JavaScript do TypeScript zespół zaczął używać protected, sądząc, że chroni to przed użyciem pól poza klasami potomnymi. Jednak programista przez Object.assign przypadkowo nadpisał pole protected w czasie działania i otrzymał trudny do wykrycia błąd. To stało się możliwe, ponieważ nie było inkapsulacji w czasie działania.