Programmiersprache TypeScript 5.0 bringt neues Konzept für Decorators

Die Beta des TypeScript-Release übernimmt für Decorators zu Methoden, Klassen und Properties den aktuellen Stand des geplanten ECMAScript-Standards.

In Pocket speichern vorlesen Druckansicht 22 Kommentare lesen

(Bild: Shutterstock)

Lesezeit: 4 Min.
Von
  • Rainald Menge-Sonnentag
Inhaltsverzeichnis

Microsoft hat die erste Beta von TypeScript 5.0 veröffentlicht. Das Release erneuert das Decorator-Konzept basierend auf den Plänen für ECMAScript. Außerdem lässt sich die Typ-Inferenz von Parametern einschränken und alle Enumerations sind nun als Unions angelegt.

Trotz des Sprungs auf eine vermeintlich neue Hauptversion gibt es im Vergleich zu dem im November 2022 veröffentlichten TypeScript 4.9 keine größeren Änderungen, die zu Inkompatibilitäten führen. Das Team folgt lediglich der bisherigen Zählweise, in der es die Nebenversionen wie Nachkommastellen zählt. Vor zweieinhalb Jahren war TypeScript 4.0 der Nachfolger von Version 3.9.

Decorators dienen dazu, vorhandenen Objekten neue Funktionen zu geben, ohne ihre ursprüngliche Struktur zu ändern. TypeScript bietet das Konzept bereits seit langem als experimentelles Feature an. Version 5 bringt einen neu erarbeiteten Ansatz, der auf Stage 3 des zugehörigen ECMAScript-Proposal beruht. Dieser weicht in einigen Punkten von der zweiten Stufe ab, die als Grundlage für die ursprüngliche Implementierung in TypeScript diente.

Die Decorators sind Funktionen, die für Klassen oder deren Funktionen und Properties aufgerufen werden. Sie können Elemente ersetzen, initialisieren oder erweiterten Zugriff darauf bieten. Ein typisches Beispiel, das sich auch im ECMAScript-Proposal findet, ist das Ersetzen einer Methode durch eine generelle Methode, die bei jedem Aufruf eine Meldung auf der Konsole ausgibt, um Fehler aufzuspüren. Eine simple Variante ohne Type-Checking findet sich im TypeScript-Blog:

function loggedMethod(originalMethod: any, _context: any) {

    function replacementMethod(this: any, ...args: any[]) {
        console.log("LOG: Entering method.")
        const result = originalMethod.call(this, ...args);
        console.log("LOG: Exiting method.")
        return result;
    }

    return replacementMethod;
}

Eine mit @loggedMethod dekorierte Methode nutzt diese Funktion als Ersatz. Sie gibt zunächst LOG: Entering method. aus, führt dann die ursprüngliche Methode aus (originalMethod.call), um abschließend LOG: Exiting method. auf die Konsole zu schreiben:

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }

    @loggedMethod
    greet() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}

const p = new Person("Ray");
p.greet();

// Output:
//
//   LOG: Entering method.
//   Hello, my name is Ray.
//   LOG: Exiting method.

Die Decorator-Funktion erhält neben der ursprünglichen Methode zusätzlich den Kontext als Parameter. Über ihn lässt sich unter anderem der Name der ursprünglichen Methode ermitteln oder abfragen, ob sie als privat deklariert ist. Für obiges Beispiel könnte die Funktion damit nicht nur anzeigen, dass die Methode aufgerufen wurde, sondern dabei ihren Namen anzeigen:

console.log(`LOG: Entering method '${methodName}'.`)

Im TypeScript-Blog findet sich zudem noch ein erweitertes Beispiel, das auf die richtige Typisierung der Parameter achtet.

Mit der neuen Umsetzung lassen sich Decorators ohne das bisher erforderliche Compiler-Flag --experimentalDecorators verwenden. Ist es dennoch gesetzt, greift TypeScript auf die alte Implementierung zurück, die auch noch eine Weile erhalten bleiben soll.

Das Ausgeben der Metadaten der Decorators über --emitDecoratorMetadata funktioniert in der neuen Implementierung bisher nicht, da es derzeit noch nicht Teil von Stage 3 der ECMAScript-Umsetzung ist. Auch lassen sich anders als bei der alten Implementierung keine Parameter dekorieren.

Neben dem neuen Decorator-Konzept bringt TypeScript 5.0 ein paar weitere nennenswerte Ergänzungen. Unter anderem kann eine Parameterdeklaration als konstant deklariert sein, um eine möglichst exakte Typinferenz zu gewährleisten:

type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): 
  T["names"] {
    return arg.names;
}

// Inferred type: readonly ["Alice", "Bob", "Eve"]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });

Das Schlüsselwort const in der zweiten Zeile sorgt dafür, dass TypeScript den exakten Typen erfasst. Ohne den Zusatz wäre der Typ schlicht string[]. Das in dieser Form verwendete const bedeutet nicht, dass der Wert unveränderlich sein muss. Dafür wäre zusätzlich das Keyword readonly erforderlich.

Zu beachten ist zudem, dass die Umsetzung nur für die direkt im Aufruf geschriebenen Elemente gilt. Für zuvor in einem Array angelegte Werte greift TypeScript auf die allgemeine Typ-Inferenz zurück.

Enumerations sind ab TypeScript 5.0 immer Union Enums und nicht mehr im Zweifel numerische Konstanten wie bei der ursprünglichen Implementierung, bevor TypeScript 2.0 eigene Typen für Enumerations eingeführt hatte.

Eine weitere Neuerung betrifft die Konfigurationsdatei für Projekte. Diese dürfen nun unter "extends" auf mehr als eine Grundlage zurückgreifen.

Im Zusammenspiel mit JSDoc kennt TypeScript 5.0 zwei neue Auszeichnungen: @overload für überladene Funktionen und @satisfies für den in TypeScript 4.9 eingeführten satisfies-Operator.

Weitere Neuerungen in TypeScript 5.0 lassen sich dem Microsofts Developer-Blog entnehmen. Wer die Beta testen möchte, kann sie von NuGet herunterladen oder über npm mit dem Befehl npm install typescript installieren. Das endgültige Release von TypeScript 5.0 ist für den 14. März geplant.

(rme)