Fortgeschritten
Dekoratoren in TypeScript: Klassen und Methoden erweitern
Wie TypeScript-Dekoratoren funktionieren, wie du eigene Dekoratoren schreibst und wo sie in der Praxis eingesetzt werden. Inklusive Beispiele für Klassen-, Methoden- und Parameter-Dekoratoren.
Inhalt
Dekoratoren sind ein mächtiges Werkzeug in TypeScript, das es erlaubt, Klassen und ihre Bestandteile zur Definitionszeit zu verändern oder zu erweitern. Sie ermöglichen saubere, wiederverwendbare Querschnittsfunktionen wie Logging, Validierung oder Caching, ohne den eigentlichen Logik-Code zu verschmutzen.
Was sind Dekoratoren?
Ein Dekorator ist im Grunde eine Funktion, die auf eine Klasse, Methode, Eigenschaft oder einen Parameter angewendet wird. Die Syntax verwendet das @-Symbol direkt vor dem dekorierten Element:
function protokollieren(ziel: any, kontext: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log(`Methode aufgerufen: ${String(kontext.name)}`);
return ziel.apply(this, args);
};
}
class Rechner {
@protokollieren
addieren(a: number, b: number): number {
return a + b;
}
}
const rechner = new Rechner();
rechner.addieren(3, 4);
// Ausgabe: Methode aufgerufen: addieren
Der Dekorator protokollieren wird auf die Methode addieren angewendet und fügt automatisch eine Konsolenausgabe hinzu, ohne die Methode selbst zu verändern.
Klassen-Dekoratoren
Ein Klassen-Dekorator wird auf die gesamte Klasse angewendet und erhält den Klassenkonstruktor als Argument:
function versiegelbar<T extends { new (...args: any[]): object }>(klasse: T, _kontext: ClassDecoratorContext) {
return class extends klasse {
constructor(...args: any[]) {
super(...args);
Object.seal(this);
}
};
}
@versiegelbar
class Konfiguration {
host = "localhost";
port = 3000;
}
const config = new Konfiguration();
config.host = "produktion.beispiel.de"; // Geht
// config.neueEigenschaft = "test"; // Fehler: Objekt ist versiegelt
Klassen-Dekoratoren eignen sich für Querschnittsfunktionen, die auf alle Instanzen einer Klasse angewendet werden sollen.
Methoden-Dekoratoren: Caching implementieren
Ein praktisches Anwendungsbeispiel ist das automatische Caching von Methodenergebnissen:
function cache(
ziel: Function,
kontext: ClassMethodDecoratorContext
) {
const cacheSpeicher = new Map<string, unknown>();
return function (this: unknown, ...args: unknown[]) {
const schluessel = JSON.stringify(args);
if (cacheSpeicher.has(schluessel)) {
console.log("Aus Cache geladen");
return cacheSpeicher.get(schluessel);
}
const ergebnis = ziel.apply(this, args);
cacheSpeicher.set(schluessel, ergebnis);
return ergebnis;
};
}
class DatenService {
@cache
berechneAufwändigerWert(n: number): number {
// Aufwendige Berechnung...
return n * n * n;
}
}
Das Caching geschieht völlig transparent. Der Aufrufer muss sich nicht darum kümmern und die Methode selbst bleibt unverändert.
Eigenschafts-Dekoratoren
Eigenschafts-Dekoratoren erlauben es, den Zugriff auf Klassen-Eigenschaften zu kontrollieren:
function nichtLeer(ziel: undefined, kontext: ClassFieldDecoratorContext) {
return function (this: unknown, initialwert: string): string {
if (!initialwert || initialwert.trim() === "") {
throw new Error(`Eigenschaft ${String(kontext.name)} darf nicht leer sein`);
}
return initialwert;
};
}
class Benutzer {
@nichtLeer
name: string = "Max Mustermann";
constructor(name: string) {
this.name = name;
}
}
const benutzer = new Benutzer("Anna Schmidt"); // OK
// const ungueltig = new Benutzer(""); // Wirft Error
Dekoratoren kombinieren
Mehrere Dekoratoren können gestapelt werden. Sie werden von unten nach oben angewendet:
function zeitMessen(ziel: Function, _kontext: ClassMethodDecoratorContext) {
return function (this: unknown, ...args: unknown[]) {
const start = performance.now();
const ergebnis = ziel.apply(this, args);
const ende = performance.now();
console.log(`Dauer: ${(ende - start).toFixed(2)}ms`);
return ergebnis;
};
}
class AnalyseService {
@protokollieren
@zeitMessen
@cache
analysiere(daten: number[]): number {
return daten.reduce((a, b) => a + b, 0);
}
}
Die Reihenfolge der Anwendung ist hier wichtig: cache wird zuerst angewendet, dann zeitMessen, dann protokollieren.
Praxis: Dependency Injection
In Frameworks wie Angular oder NestJS sind Dekoratoren die Grundlage für Dependency Injection:
function injizierbar(_klasse: Function, _kontext: ClassDecoratorContext) {
// Framework registriert die Klasse als injectable Service
console.log(`${_klasse.name} als Service registriert`);
}
@injizierbar
class EmailService {
sende(an: string, betreff: string): void {
console.log(`E-Mail an ${an}: ${betreff}`);
}
}
Fazit
Dekoratoren sind ein elegantes Mittel, um Querschnittsfunktionen sauber vom Geschäftscode zu trennen. Sie machen Code lesbarer und vermeiden Wiederholungen. Mit TypeScript 5.0 sind sie näher am Standard und damit zukunftssicher. Logging, Validierung, Caching und Dependency Injection sind die häufigsten Einsatzgebiete.
Häufige Fragen
Sind TypeScript-Dekoratoren bereits ein offizieller JavaScript-Standard?
Dekoratoren wurden im Jahr 2024 als Stage-3-Proposal in JavaScript aufgenommen. TypeScript 5.0 implementiert diese neue Version. Die ältere experimentelle Variante mit experimentalDecorators ist davon verschieden.
Was ist der Unterschied zwischen experimentalDecorators und den neuen Dekoratoren in TypeScript 5?
Die experimentellen Dekoratoren (bis TypeScript 4.x) folgen dem alten Proposal und benötigen das Flag experimentalDecorators. Ab TypeScript 5.0 gibt es eine neue Implementierung, die näher am offiziellen TC39-Standard liegt und ohne dieses Flag auskommt.
Kann ich Dekoratoren für einfache Objekte oder Funktionen verwenden?
Nein, Dekoratoren in TypeScript gelten nur für Klassen, Klassenmethoden, Klasseneigenschaften, Accessor und Parameter. Einfache Funktionen oder Objekte werden nicht unterstützt.
Quellen
- TypeScript 5.0 Release Notes: Decorators (typescriptlang.org)
- TC39 Decorator Proposal (github.com/tc39/proposal-decorators)
Über die Autorenschaft
Mateusz Viola
Betreiber und redaktionelle Verantwortung typescript-playground.de
Themengebiet: Mathematik, Kalenderrechnung, Schaltjahre, Statistik und ISO 8601
Mehr über Mateusz Viola →Verwandte Artikel
Grundlagen
Was ist TypeScript? Eine Einführung in die typisierte JavaScript-Erweiterung
TypeScript erweitert JavaScript um statische Typen und macht große Codebases wartbarer. Lerne die Grundlagen, die Geschichte und die wichtigsten Vorteile von TypeScript.
Lesezeit 7 Min.
Grundlagen
TypeScript vs. JavaScript: Die wichtigsten Unterschiede im Überblick
TypeScript und JavaScript unterscheiden sich grundlegend in der Typprüfung, Werkzeugunterstützung und Wartbarkeit. Dieser Ratgeber erklärt die Unterschiede und hilft bei der Wahl der richtigen Sprache.
Lesezeit 8 Min.
Typen
TypeScript Typen Grundlagen: string, number, boolean und mehr
Die primitiven Typen string, number und boolean sind das Fundament von TypeScript. Dieser Ratgeber erklärt alle grundlegenden Typen mit praktischen Beispielen.
Lesezeit 9 Min.