TypeScript Decorators: Klassen, Methoden und Eigenschaften

TypeScript Decorators sind eine praktische und einfache Methode, um Objekten zusätzliche Funktionen zuzuweisen, ohne dabei den Quellcode zu verändern. Sie können auf Klassen, Methoden, Eigenschaften, Zugriffsfunktionen und Parameter angewendet werden und greifen auf Annotationen und Metadaten zu.

Was sind TypeScript Decorators und wofür werden sie genutzt?

Das Prinzip der TypeScript Decorators ist grundsätzlich nicht neu. In anderen Programmiersprachen finden sich ähnliche Features wie zum Beispiel Attribute in C#, Decorators in Python oder Annotationen in Java. Es handelt sich dabei um eine Möglichkeit, die Funktionalität eines Objekts zu erweitern, ohne dabei den Quellcode zu verändern. Auch TypeScript arbeitet bereits seit längerer Zeit mit diesem Ansatz. Zwar unterstützen die meisten Browser TypeScript Decorators (noch) nicht, es lohnt sich aber trotzdem, diese Herangehensweise und ihre Möglichkeiten auszuprobieren. Seit Version 5.0 wurde der Einsatz der Dekoratoren noch einmal massiv vereinfacht.

TypeScript Decorators werden genutzt, um Anmerkungen (Annotations) und zusätzliche Metadaten für TypeScript Classes und Elemente hinzuzufügen. Dabei lassen sich neben den Klassen auch Methoden, Eigenschaften, Zugriffsmethoden und Parameter verändern. Letztere können dabei überprüft werden und ihre Werte lassen sich abrufen. Das ist auch ein großer Unterschied zwischen TypeScript Decorators und ihrem Äquivalent für JavaScript.

Managed Nextcloud by IONOS Cloud
Teamarbeit in der eigenen Cloud
  • Vollständige Datensouveränität in deutschen Rechenzentren
  • Managed Service ohne Administrationsaufwand
  • File-Sharing, Dokumentenbearbeitung & Kommunikation

Syntax und Funktionsweise der Dekoratoren

Fügen Sie TypeScript Decorators zu einem Objekt hinzu, rufen Sie damit strenggenommen eine Funktion auf, die ohne eine Änderung des Quellcodes ausgeführt werden kann. Sie erhöhen somit die Funktionalität und halten den Code trotzdem übersichtlich. Die grundsätzliche Syntax sieht wie folgt aus:

@nameDesDekorators
typescript

Diese Funktion erstellen Sie entweder mit zwei oder drei Parametern. Die Syntax für die Funktion mit drei Parametern sieht so aus:

function decoratorFunktion(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(`Decorating ${propertyKey} of class ${target.constructor.name}`);
}
class MyClass {
    @decoratorFunktion
    myMethod() {
    }
}
typescript

Die einzelnen Bestandteile der TypeScript Decorators setzen sich wie folgtzusammen:

  • target: target bezeichnet das Objekt, dem der Dekorator zugewiesen wurde
  • propertyKey: propertyKey ist ein String, der den Namen der Klasse enthält, der ein Dekorator zugewiesen wurde; möglich sind unter anderem Methoden oder Eigenschaften
  • descriptor: Unter descriptor werden weitere Informationen über das Objekt hinterlegt, auf das der Dekorator angewendet wird; mögliche Eigenschaften sind value, writable, enumerable oder configurable.

Die Syntax von TypeScript Decorators mit zwei Parametern sehen Sie hier:

function decoratorFunktion(target: any) {
    console.log(`Decorating ${target.name}`);
}
@decoratorFunktion
class MyClass {
}
typescript

In diesem Fall wurde TypeScript Decorators auf eine Klasse angewandt.

Die unterschiedlichen Typen von Dekoratoren

Es gibt verschiedene Arten von TypeScript Decorators, die wir Ihnen im weiteren Verlauf genauer vorstellen und die alle Besonderheiten aufweisen:

  • Class Decorators
  • Method Decorators
  • Property Decorators
  • Accessor Decorators
  • Parameter Decorators

TypeScript Decorators für Klassen

Möchten Sie die Eigenheiten einer Klasse anpassen und ihren Konstruktor, ihre Methoden oder Eigenschaften ändern, ist dies mit TypeScript Decorators möglich. Sie erhalten dabei als ersten Parameter den Konstruktor, sobald Sie die Klasse mit einer Funktion „dekorieren“. Dies ist ein beispielhafter Code, bei dem wir mit einer Kundenliste arbeiten. Sie hat einige private und einige öffentliche Eigenschaften:

class Kunden {
    private static userType: string = "Generic";
    private _email: string;
    public kundenname: string;
    public strasse: string = "";
    public wohnort: string = "";
    public land: string = "";
    constructor(kundenname: string, email: string) {
        this.kundenname = kundenname;
        this._email = email;
    }
    Static get userType() {
        return Kunden.userType;
    }
    get email() {
        return this._email;
    }
    set email(neueEmail: string) {
        this._email = neueEmail;
    }
    adresse(): string {
        return `${this.strasse}\n${this.wohnort}\n${this.land}`;
    }
}
const p = new Kunden("beispielKunde", "name@beispiel.com");
p.strasse = "Hofgasse 2";
p.wohnort = "Berlin";
typescript

Im nächsten Schritt nutzen wir nun TypeScript Decorators, um weitere Funktionen hinzuzufügen, die den Quellcode allerdings nicht nachträglich verändern. So fügen wir für die Klasse „Kunden“ den Dekorator @frozen hinzu. Diese Funktion sorgt dafür, dass die Objekte sich nicht nachträglich verändern lassen. Für einige Eigenschaften nutzen wir @required, um die Eingabe ausdrücklich anzuweisen. Des Weiteren verwenden wir @enumerable für Auflistungen und @deprecated für veraltete Eingaben. Zunächst kümmern wir uns um die Definition der Dekoratoren:

function frozen(constructor: Function) {
    Object.freeze(constructor);
    Object.freeze(constructor.prototype);
}
function required(target: any, propertyKey: string) {
    // Logik für Required-Decorator
}
function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
}
function deprecated(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.warn(`The method ${propertyKey} is deprecated.`);
}
typescript

Nach dem Einsatz von TypeScript Decorators sieht der abschließende Code dann wie folgt aus:

@frozen
class Kunden {
    private static userType: string = "Generic";
    @required
    private _email: string;
    @required
    public kundenname: string;
    public strasse: string = "";
    public wohnort: string = "";
    public land: string = "";
    constructor(kundenname: string, email: string) {
        this.kundenname = kundenname;
        this._email = email;
    }
    @enumerable(false)
    get userType() {
        return Kunden.userType;
    }
    get email() {
        return this._email;
    }
    set email(neueEmail: string) {
        this._email = neueEmail;
    }
    @deprecated
    addresse(): string {
        return `${this.strasse}\n${this.wohnort}\n${this.land}`;
    }
}
const p = new Kunden("beispielKunde", "name@beispiel.com");
p.strasse = "Hofgasse, 2";
p.wohnort = "Berlin";
typescript

TypeScript Decorators für Methoden

Auch für Methoden ist der Einsatz von TypeScript Decorators möglich. Ausnahmen sind Declaration Files, das Overloading oder die Klasse „declare“. Im folgenden Beispiel nutzen wir @enumerable als Dekorator für die Methode getName in der Klasse „Person“:

const enumerable = (value: boolean) => {
    return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
        propertyDescriptor.enumerable = value;
    }
}
class Person {
    vorname: string = "Julia"
    nachname: string = "Schulz"
    @enumerable(true)
    getName () {
        return `${this.vorname} ${this.nachname}`;
    }
}
typescript

TypeScript Decorators für Propertys

TypeScript Decorators für die Eigenschaften einer Klasse (Property Decorators) haben zwei Parameter: die Konstruktor-Funktion der Klasse und den Namen der Eigenschaft. Im folgenden Beispiel nutzen wir den Dekorator, um den Namen einer Eigenschaft (hier der Kundenname) auszugeben:

const printPropertyName = (target: any, propertyName: string) => {
    console.log(propertyName);
};
class Kunden {
    @printPropertyName
    name: string = "Julia";
}
typescript

TypeScript Decorators für Zugriffsfunktionen

Accessor Decorators funktionieren nach einem ganz ähnlichen Prinzip wie Property Decorators. Im Vergleich zu diesen haben sie einen zusätzlichen dritten Parameter. In unserem Beispiel handelt es sich dabei um den Property Descriptor für einen Kunden oder eine Kundin. Geben Sie nun mit dem Accessor Decorator einen Wert aus, wird dieser zum neuen Property Descriptor. Im folgenden Code wird so der boolesche Wert („true“ oder „false“) von enumerable geändert. Dies ist unser Ausgangspunkt:

const enumerable = (value: boolean) => {
    return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
        descriptor.enumerable = value;
    }
}
typescript

So setzen Sie den Dekorator ein:

class Kunden {
    vorname: string = "Julia";
    nachname: string = "Schulz";
    @enumerable(true)
    get name() {
        return `${this.vorname} ${this.nachname}`;
    }
}
typescript

TypeScript Decorators für Parameter

TypeScript Decorators vom Typ Parameter Decorator verfügen ebenfalls über drei Parameter: die Konstruktor-Funktion der Klasse, den Namen der Methode und zusätzlich eine Indexbezeichnung des Parameters. Der Parameter selbst kann allerdings nicht geändert werden, sodass dieser Dekorator nur zur Überprüfung verwendet werden kann. Möchten Sie zum Beispiel den Index erfragen, funktioniert das mit diesem Code:

function print(target: Object, propertyKey: string, parameterIndex: number) {
    console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}
typescript

Wenden Sie dann den Parameter Decorator an, ist dies der Code:

class Beispiel {
    testMethod(param0: any, @print param1: any) {}
}
typescript
Tipp

Ideal für statische Websites und Apps gleichermaßen: Mit Deploy Now von IONOS profitieren Sie von einfachem Staging, einem schnellen Setup und perfekt abgestimmten Workflows. Finden Sie das passende Modell für Ihre Zwecke!

Page top