33 Decorators Deep Dive in TypeScript

Decorators sind ein experimentelles und fortschrittliches Feature in TypeScript, das aus dem Vorschlag für ECMAScript entstanden ist. Sie ermöglichen Anmerkungen und eine metaprogrammierungsbasierte syntaktische Art der Klassendeklarationen und erfordern das Compiler-Flag "experimentalDecorators": true in der tsconfig.json.

33.1 Status als “Experimentelles Feature”

Decorators in TypeScript basieren auf einem ECMAScript-Vorschlag und befinden sich im Stadium 2 des ECMAScript-Vorschlagsprozesses. Da sie noch nicht endgültig im ECMAScript-Standard verankert sind, werden sie in TypeScript als experimentell behandelt, was bedeutet, dass sich ihre Implementierung in Zukunft ändern könnte.

33.2 Klassen-Decorators

Ein Klassen-Decorator wird direkt vor einer Klassendeklaration platziert. Er kann den Konstruktor erweitern und Metadaten hinzufügen:

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

33.3 Method-Decorators

Method-Decorators werden vor Methodendeklarationen angewendet:

function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    let originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log("Calling " + propertyName);
        return originalMethod.apply(this, args);
    }
}

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }

    @log
    greet() {
        return "Hello, " + this.greeting;
    }
}

33.4 Accessor-Decorators

Accessor-Decorators werden auf Getter und Setter angewendet:

function configurable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = value;
    };
}

class Point {
    private _x: number;
    private _y: number;

    constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }

    @configurable(false)
    get x() { return this._x; }

    @configurable(false)
    get y() { return this._y; }
}

33.5 Property-Decorators

Property-Decorators werden vor Eigenschaftsdeklarationen platziert:

function defaultToZero(target: any, propertyName: string) {
    Object.defineProperty(target, propertyName, {
        value: 0,
        writable: true
    });
}

class Example {
    @defaultToZero
    count: number;
}

33.6 Parameter-Decorators

Parameter-Decorators werden direkt vor einem Parameterdeklarator verwendet:

function validate(target: any, propertyName: string, index: number) {
    // Validierungslogik hier
}

class Greeter {
    greet(@validate message: string) {
        return "Hello, " + message;
    }
}

Vielen Dank für das bereitgestellte Beispiel. Ich werde es verwenden, um den Abschnitt über Funktionsdecorators in Ihrem Artikel zu überarbeiten. Hier ist die aktualisierte Version dieses Abschnitts:


33.7 Funktions-Decorators

Funktions-Decorators sind in TypeScript eine leistungsfähige Möglichkeit, zusätzliche Funktionalität vor und nach der Ausführung einer Methode einzufügen. Sie werden direkt vor der Methodendeklaration platziert und können das Verhalten der Methode durch Modifikation des PropertyDescriptor beeinflussen.

Das folgende Beispiel zeigt, wie ein Funktionsdecorator implementiert werden kann, um den Aufruf und die Ergebnisse einer Methode zu protokollieren:

function Log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
        // Logik vor der Ausführung der Methode
        console.log(`Aufruf von ${propertyKey} mit Argumenten: ${JSON.stringify(args)}`);
        
        // Ausführung der ursprünglichen Methode
        const result = originalMethod.apply(this, args);
        
        // Logik nach der Ausführung der Methode
        console.log(`${propertyKey} wurde ausgeführt und gab zurück: ${result}`);
        
        return result;
    };
}

class MyClass {
    @Log
    myMethod(arg: string) {
        console.log(`In myMethod: Argument = ${arg}`);
        return `Result: ${arg}`;
    }
}

const myObj = new MyClass();
myObj.myMethod("Testargument");

In diesem Beispiel wird der @Log-Decorator verwendet, um den Methodenaufruf von myMethod in der Klasse MyClass zu protokollieren. Der Decorator fängt den Aufruf der Methode ab, protokolliert die übergebenen Argumente, führt die Methode aus, protokolliert das Ergebnis und gibt das Ergebnis zurück. Dies ist besonders nützlich für Debugging-Zwecke, Performance-Messungen oder zur Auditierung von Methodenaufrufen.