Dekoratory w TypeScript to zaawansowana funkcjonalność, która pozwala na modyfikowanie zachowania klas, metod, pól lub parametrów funkcji. Dekoratory dodają do kodu dodatkową warstwę abstrakcji i są szczególnie przydatne w frameworkach takich jak Angular czy React, gdzie często wykorzystywane są do modyfikowania komponentów, usług, czy funkcji.
Dekorator w TypeScript to funkcja, którą można przypisać do konkretnej części kodu, aby zmienić jego zachowanie bez potrzeby zmiany wewnętrznej logiki.
Czym są dekoratory?
Dekorator to specjalna funkcja, która może zostać przypisana do:
Klasy
Metody
Pola
Parametru funkcji
Dekorator jest wywoływany w momencie deklaracji klasy, a nie w czasie jej działania, co pozwala na wprowadzanie zmian w strukturze i zachowaniu klasy przed jej faktycznym użyciem.
Dekoratory w TypeScript są obecnie funkcjonalnością eksperymentalną, dlatego muszą być włączone w ustawieniach kompilatora. Można to zrobić, ustawiając opcję "experimentalDecorators": true w pliku tsconfig.json.
Jak działają dekoratory?
Dekorator to funkcja, która jest wywoływana z trzema podstawowymi parametrami:
Target – klasa, metoda lub pole, do którego dekorator jest przypisany.
PropertyKey – nazwa dekorowanej metody lub pola.
Descriptor – opis metody lub pola.
Typy dekoratorów
TypeScript oferuje kilka typów dekoratorów, każdy mający swoje specyficzne zastosowanie:
Dekoratory klas – dla modyfikacji całych klas.
Dekoratory metod – dla modyfikacji pojedynczych metod.
Dekoratory pól – dla modyfikacji właściwości klasy.
Dekoratory parametrów – dla modyfikacji parametrów funkcji.
Dekoratory klas
Dekorator klasy służy do modyfikacji lub rozszerzenia zachowania klasy. Jest wywoływany z jednym argumentem, którym jest konstruktor dekorowanej klasy.
Przykład:
function Component(constructor: Function) {
console.log("Dekorator klasy wywołany!");
}
@Component
class MyComponent {
constructor() {
console.log("Instancja klasy stworzona.");
}
}
const component = new MyComponent();
// Wypisze: "Dekorator klasy wywołany!"
// Wypisze: "Instancja klasy stworzona."
W powyższym przykładzie dekorator @Component jest przypisany do klasy MyComponent, a jego funkcja jest wywoływana w momencie definiowania klasy. Umożliwia to wprowadzanie dodatkowej logiki podczas tworzenia klasy, np. logowanie, rejestrowanie komponentów, czy modyfikowanie struktury klasy.
Dekoratory metod
Dekoratory metod pozwalają na modyfikowanie zachowania konkretnych metod w klasie. Są szczególnie przydatne, gdy chcemy np. dodać walidację, logowanie, obsługę błędów, czy kontrolę dostępu do metody.
Dekorator metod otrzymuje trzy parametry:
Target – prototyp klasy lub konstruktor.
PropertyKey – nazwa dekorowanej metody.
Descriptor – opis metody (np. jej właściwości writable, enumerable, configurable).
Przykład:
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Metoda ${propertyKey} została wywołana z argumentami: ${args}`);
return originalMethod.apply(this, args);
};
}
class Calculator {
@LogMethod
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // Wypisze: "Metoda add została wywołana z argumentami: 2,3"
Tutaj dekorator @LogMethod modyfikuje metodę add, dodając logowanie jej wywołań wraz z argumentami. Dekorator otrzymuje dostęp do opisu metody (descriptor), dzięki czemu może modyfikować jej zachowanie.
Dekoratory pól
Dekoratory pól pozwalają na modyfikowanie właściwości klas. Dekorator pola nie ma dostępu do wartości pola, ale może zmieniać sposób, w jaki jest ono definiowane.
Dekorator pola otrzymuje dwa parametry:
Target – prototyp klasy (dla pól instancji) lub konstruktor klasy (dla pól statycznych).
PropertyKey – nazwa pola.
function ReadOnly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Person {
@ReadOnly
name: string = "Jan";
}
const person = new Person();
person.name = "Adam"; // Błąd: Cannot assign to 'name' because it is a read-only property
W powyższym przykładzie dekorator @ReadOnly ustawia pole name jako writable: false, co sprawia, że jest ono niezmienne po przypisaniu początkowej wartości.
Dekoratory parametrów
Dekoratory parametrów pozwalają na modyfikowanie poszczególnych parametrów metody. Mogą być używane np. do wstrzykiwania zależności lub walidacji danych wejściowych.
Dekorator parametrów otrzymuje trzy parametry:
Target – prototyp klasy.
PropertyKey – nazwa metody, której parametr jest dekorowany.
ParameterIndex – indeks dekorowanego parametru.
Przykład:
function LogParameter(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parametr w metodzie ${propertyKey} na pozycji ${parameterIndex} został oznaczony dekoratorem.`);
}
class Example {
sayHello(@LogParameter name: string) {
console.log(`Witaj, ${name}!`);
}
}
const example = new Example();
example.sayHello("Jan");
// Wypisze: "Parametr w metodzie sayHello na pozycji 0 został oznaczony dekoratorem."
// Wypisze: "Witaj, Jan!"
Dekorator @LogParameter informuje nas, że parametr funkcji sayHello został oznaczony. Chociaż w przykładzie ten dekorator tylko loguje informacje, w bardziej zaawansowanych przypadkach może być używany do walidacji lub modyfikacji parametrów.
Praktyczne zastosowania dekoratorów
Dekoratory są szeroko wykorzystywane w nowoczesnych frameworkach takich jak Angular. Umożliwiają tam modyfikację komponentów, usług, modułów, czy dyrektyw bez potrzeby ingerencji w ich wewnętrzny kod.
W Angularze przykładem użycia dekoratora jest @Component, który oznacza klasę jako komponent Angulara, dostarczając jej metadanych takich jak selektor, szablon HTML, style itp.
Przykład w Angularze:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
}
Dekorator @Component przypisuje metadane do klasy AppComponent, dzięki czemu Angular wie, że ma do czynienia z komponentem, który ma swoje przypisane szablony i style.
Podsumowanie
Dekoratory w TypeScript to potężna technika, która pozwala modyfikować zachowanie klas, metod, pól oraz parametrów. Dzięki dekoratorom możemy wprowadzać dodatkowe funkcjonalności, takie jak logowanie, walidacja, czy kontrola dostępu, bez modyfikowania wewnętrznej logiki aplikacji. Choć dekoratory są funkcjonalnością eksperymentalną, to stanowią integralną część wielu frameworków, takich jak Angular.
Najważniejsze typy dekoratorów to:
Dekoratory klas – modyfikowanie klas.
Dekoratory metod – modyfikowanie metod.
Dekoratory pól – modyfikowanie właściwości klas.
Dekoratory parametrów – modyfikowanie poszczególnych parametrów metod.
Dekoratory znacznie ułatwiają pisanie elastycznego i łatwego do rozszerzania kodu, co czyni je nieocenionym narzędziem w nowoczesnym programowaniu z użyciem TypeScript.