Prywatne pola i metody JS vs TS
Czy private w TS jest tym samym co hash w JS?
Często słyszę od programistów: “Nie po to używam TS, aby pisać w JS!” czy “Używam private w TS, po co mam to robić w JS!?""
Czy jest to tylko kosmetyka i nie ma żadnej różnicy? Nie…
“Prywatność” w JavaScript wprowadził standard:
ECMAScript 2022
#name = 0;
#getName()
get #getName();
set #setName();
TypeScript
private name:number = 0;
private getName():void
Kiedyś, aby w JS zrobić pole prywatne/metodę stosowano tzw. leading underscore przed polem, ale to tylko było dla czytelności, nie miało to żadnego technicznego zastosowania.
TypeScript jak wiemy, działa tylko do momentu transpilacji kodu, chroni nas kod na czas pisania, po transpilacji kod zamienia się na czysty JavaScript. I wszystkie interfejsy, typy, modyfikatory dostępu są usuwane.
…no, chyba że mówimy o enumach to tutaj sprawa wygląda inaczej, napisałem o tym osobny artykuł (Dlaczego unikam typów wyliczeniowych w TypeScript?).
Przykład
TypeScript
class Test {
private fieldTS: number = 0;
#fieldJS: string = "Testowy";
private methodTS(): number {
return 123;
}
#methodJS(): number {
return 123;
}
}
wynik od ECMAScript 2022
"use strict";
class Test {
fieldTS = 0;
#fieldJS = "Testowy";
methodTS() {
return 123;
}
#methodJS() {
return 123;
}
}
Można by się spodziewać, ze private po transpilacji zamieni się na pole z #, niestety tak się nie stało, wszystko zostało usunięte.
Jak widzimy na przykładzie, między innymi modyfikator private został całkowicie usunięty, pole zrobiło się publiczne i dostępne na zewnątrz, pole z hashem nawet po transpilacji zostało i mamy bezpieczeństwo typu.
Teraz gdy na wynikowym pliku zrobimy wywołanie:
"use strict";
class Test {
fieldTS = 0;
#fieldJS = "Testowy";
methodTS() {
return 123;
}
#methodJS() {
return 123;
}
}
const t = new Test();
t.fieldTS;
t.methodTS();
t.#fieldJS;
t.#methodJS();
Teraz gdy chcemy się odwołać do pola prywatnego z # dostajemy w konsoli: Uncaught SyntaxError: Private field ‘#fieldJS’ must be declared in an enclosing class
Obejście
Warto zaznaczyć, że nawet w czasie pisania możemy to w łatwy sposób obejść stosując rzutowanie klasy na any:
class Test {
private fieldTS: number = 0;
#fieldJS: string = "Testowy";
private methodTS(): number {
return 123;
}
#methodJS(): number {
return 123;
}
}
const test = new Test();
console.log((test as any).fieldTS); //0
(test as any).fieldTS = 68;
console.log((test as any)['fieldTS']); // 68
Podsumowanie
javascript
- zawsze prywatny,
- bezpieczeństwo,
- działa tylko wewnątrz klasy, świat i podklasy nie mają dostępu,
- dobre dla bibliotek, daje dostęp do tylko publicznych metod, pól zwiększa odporność na błędne działanie biblioteki
typescript
- może być uznawane za bardziej czytelne,
- brak bezpieczeństwa,
- można obejść na czas pisania stosując rzutowanie na any,
- działa tylko do czasu transpilacji, potem jest publiczne
Od autora
Stawiam bardziej JavaScript nad TypeScript, jeśli mogę używam natywnych rozwiązań dopiero potem uzupełniam TS.
Jak w każdym narzędziu warto być ostrożnym, bo z czasem mogą przynieść odwrotne skutki do zamierzonych.
Jak na przykład dekoratory czy enumy, o których napiszę osobny artykuł.