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.