programowanieFullstack programista

Jak działa mechanizm prywatnych pól w klasach TypeScript? Czym różnią się prywatne właściwości używające modyfikatora private i symbolu #, jakie są szczegóły ich zastosowania i ograniczenia?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

W TypeScript prywatne właściwości pojawiły się początkowo za pomocą modyfikatora private, a począwszy od ECMAScript 2021 w JavaScript wspierane są prawdziwe prywatne pola za pomocą symbolu #. Główna zasada — inkapsulacja danych, aby chronić wewnętrzne szczegóły klasy przed dostępem z zewnątrz.

Problem

Modyfikator private w TypeScript zapewnia prywatność tylko na etapie kompilacji, ale w skompilowanym JavaScript te pola są dostępne poprzez bezpośredni dostęp do właściwości obiektu. Może to prowadzić do niezamierzonej zmiany stanu obiektu. Pola prywatne ES (#) są całkowicie niedostępne poza klasą nawet na poziomie czasu wykonywania JS, co zapewnia rzeczywistą ochronę danych.

Rozwiązanie

TypeScript obsługuje oba podejścia. Wybierając między nimi, kieruj się wymaganą stopą ochrony i ograniczeniami kompatybilności z wersjami JS.

Przykład kodu:

class Example { private hidden: number; #trulyHidden: string; constructor() { this.hidden = 42; this.#trulyHidden = 'secret'; } getHidden() { return this.hidden; } getTrulyHidden() { return this.#trulyHidden; } } const x = new Example(); // x.hidden — błąd w TS, ale zadziała przez x['hidden'] w JS // x.#trulyHidden — błąd składniowy nawet w JS

Kluczowe cechy:

  • private zapewnia prywatność tylko na etapie kompilacji TypeScript
  • #field zapewnia prawdziwą prywatność na poziomie wykonania JavaScript
  • Użycie # wymaga wsparcia nowoczesnego JS (ES2021+)

Pytania z pułapką.

Czy można uzyskać dostęp do prywatnego pola klasy TypeScript poprzez bezpośrednie odwołanie w JS?

Tak, można, ponieważ prywatność jest implementowana tylko na etapie kompilacji. W pierwotnym JS pole będzie zwykłą właściwością obiektu. Na przykład:

class A { private x = 1; } const a = new A(); console.log((a as any)["x"]); // 1

Czy można używać prywatnych pól z # w interfejsach lub opisywać je za pomocą type?

Nie, pola #private są częścią tylko implementacji klasy i nie mogą być opisane w interfejsach, typach ani używane poza samą klasą. Interfejsy opisują tylko publiczne człony.

Czy można dziedziczyć prywatne pola zadeklarowane za pomocą #?

Nie, takie pola są całkowicie niedostępne dla klas dziedziczących. Tylko sama klasa ma do nich dostęp:

class Parent { #foo = 123; } class Child extends Parent { // this.#foo = 444; // Błąd }

Typowe błędy i antywzorce

  • Używanie private do ochrony krytycznych danych — prowadzi do podatności na bezpośredni dostęp
  • Mylenie prywatności na poziomie typów z czasem wykonywania
  • Próba użycia #fields w starych wersjach JS

Przykład z życia

Negatywny przypadek

W projekcie do przechowywania haseł w klasie User użyto pola private password. Jeden z programistów przypadkowo uzyskuje dostęp przez user['password'] do debugowania, a pole okazuje się być zmienione przez zewnętrzny moduł.

Zalety:

  • Łatwa integracja z TypeScript

Wady:

  • Brak rzeczywistej ochrony w czasie wykonywania JS
  • Łatwe do obejścia prywatności przez przypadek

Pozytywny przypadek

Programista używa ES prywatnego pola #password. Teraz próby odwołania się do pola poprzez indeksowanie czy w dziedziczących klasach nie przechodzą, bezpieczeństwo danych jest zapewnione nawet przy użyciu zewnętrznych bibliotek i narzędzi.

Zalety:

  • Prawdziwa prywatność
  • Solidna ochrona wrażliwych właściwości

Wady:

  • Wymaga wsparcia nowoczesnego JS (niekompatybilne z ES5)
  • Prywatne pola nie są widoczne w interfejsach, nie można ich nadpisywać w klasach dziedziczących