W porzednim wpisie ogólnie dowiedzieliśmy się do czego przydadzą się nam modyfikatory dostępu. W tym przejdzemy do konkretów czyli omówienia poziomów dostępu oferowanych prez TypeScript – public, private i protected.
Zapraszam do dołączenia za darmo do kilkugodzinnego kursu wprowadzającego do TypeScript.
Modyfikatory dostępu w TypeScript – rodzaje
TypeScript wspiera trzy typy modyfikatorów dostępu – public, private i protected. Używamy ich przed nazwami zmiennych bądź funkcji.
class Info { private name: string; private email: string; private preferences: string[]; constructor(name: string, email: string) { this.name = name; this.email = email; this.preferences = []; } public toString = () => { return `${this.name} - ${this.email}`; }; }
Domyślnym z nich jest public, więc linijka
constructor(name: string, email: string)
tłumaczy się na
public constructor(name: string, email: string)
Public
Public jest najszerszym modyfikatorem dostępu, umożliwia on dostęp do tak oznaczonego elementu wszystkim i wszystkiemu. Jest to, jak już wspominałem, domyślny poziom dostępu. Jest to też jedyny poziom dostępu w JavaScript, gdzie wszystko jest publiczne.
Poniższy kod wykona się więć bez żadnego problemu:
class Info { public name: string; public email: string; public preferences: string[]; constructor(name: string, email: string) { this.name = name; this.email = email; this.preferences = []; } public toString = () => { return `${this.name} - ${this.email}`; }; } class ClassifiedInfo extends Info { public toString = () => { return `${super.name}. Everything else is classified`; } } let info : Info = new Info("Pawel", "pawel@pawel"); console.log(info.name); info.email = "nowy@gmail"; console.log(info.toString());
Do zmiennych email, name oraz metody toString jest dostęp z poziomu klasy definiującej te pola (Info), jak i klasy rozszerzającej klasę Info (ClassifiedInfo) oraz dodatkowo z globalnego scope aplikacji.
Protected
Modyfikator protected ucina nieco swawole wyczyniające się dzięki public. Do elementów tak oznaczonych ma dostęp tylko klasa, w której zostały one zdefiniowane oraz wszelkie klasy dziedziczące po niej.
class Info { protected name: string; protected email: string; protected preferences: string[]; constructor(name: string, email: string) { this.name = name; this.email = email; this.preferences = []; } protected toString = () => { return `${this.name} - ${this.email}`; }; } class ClassifiedInfo extends Info { public toString = () => { return `${super.name}. Everything else is classified`; } } let info : Info = new Info("Pawel", "pawel@pawel"); console.log(info.name); info.email = "nowy@gmail"; console.log(info.toString());
Dostęp do pól z poziomu klasy Info i ClassifiedInfo jest wciąż dozwolony, ponieważ ClassifiedInfo jest dzieckiem klasy Info. Natomiast wywołania w przestrzeni globalnej wywołają błędy kompilatora.
Private
Private jest ostatnim z modyfikatorów dostępu w TypeScript i jest niemal lustrzanym odbiciem public. Do elementów określonych jako private dostęp jest tylko z poziomu klasy, która je deklaruję. Natomiast dostęp z klas potomnych czy przestrzeni globalnej wywoła płacz kompilatora.
class Info { private name: string; private email: string; private preferences: string[]; constructor(name: string, email: string) { this.name = name; this.email = email; this.preferences = []; } private toString = () => { return `${this.name} - ${this.email}`; }; } class ClassifiedInfo extends Info { public toString = () => { return `${super.name}. Everything else is classified`; } } let info : Info = new Info("Pawel", "pawel@pawel"); console.log(info.name); info.email = "nowy@gmail"; console.log(info.toString());
Przy tak określonym dostępie zarówno w przestrzeni globalnej, jak i w klasie ClassifiedInfo pojawią się błędy kompilacji, w klasie Info nadal wszystko będzie w porządku.
Jak używać modyfikatorów dostępu
Oczywiście robienie wszystkiego prywatnym bądź publicznym to nie jest rozwiązanie.
Najprostszym podejściem do problemu „jakiego modyfikatora dostępu użyć” jest następujący algorytm:
- Stan klasy zawsze jest prywatny.
- Konstruktor jest publiczny.
- Metody używane przez inne klasy są publiczne, natomiast wszelkie metody pomocnicze (tzn. takie używane przez implementacje metody publicznej) są prywatne.
- Jeśli musimy wystawić na zewnątrz możliwość pobrania/zmiany stanu robimy to poprzez getter/setter
class DataTransformer { private data: string; private reversed: string[]; private transformed: string; constructor(data: string) { this.data = data; this.reversed = []; this.transformed = ""; } private splitData() : void { this.reversed = this.data.split(""); } private reverseData() : void { this.reversed = this.reversed.reverse(); } private joinData() : void { this.transformed = this.reversed.join(""); } public transform() : string { this.splitData(); this.reverseData(); this.joinData(); return this.transformed; } } const trans : DataTransformer = new DataTransformer("hello"); console.log(trans.transform());
Oczywiście jak do wszystkiego jest masa wyjątków od tego prostego podejścia, ale na start jest w zupełności wystarczające.
Runtime
W trakcie działania aplikacji mamy do czynienia z JavaScript, więc niestety modyfikatory dostępu giną i wszystko jest publiczne.
Interesuje Cię TypeScript? Wprowadzenie do tego języka jest jednym z darmowych kursów dostępnych na kursy.clockworkjava.pl.