Programmiersprache TypeScript 4.7 erweitert das Modulsystem

Die Sprache kann neuerdings neben CommonJS- auch ECMAScript-Module verarbeiten und bekommt dafür neue Dateiendungen.

Lesezeit: 4 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen
Schreibmaschine, Tastatur, Keyboard, Digitalisierung

(Bild: Laszlo Zakarias, gemeinfrei)

Von
  • Rainald Menge-Sonnentag

Sechs Wochen nach der ersten Beta ist nun das endgültige Release von TypeScript 4.7 erschienen. Das JavaScript-Superset bringt die ursprünglich bereits im Oktober 2021 geplante Anbindung an ECMAScript Modules (ESM) mit und lernt dafür neue Dateiendungen. Überdies erweitert das Release die Kontrollflussanalyse für Elemente mit einem Index.

Node.js hat das ESM vor allem in der 2019 veröffentlichten Version 12 als Alternative zum traditionellen Modulsystem CommonJS (CJS) umgesetzt. Das TypeScript-Team plante die Anbindung an ESM ursprünglich in der Betaphase von Version 4.5, ruderte wegen Bedenken hinsichtlich der Usability jedoch zunächst zurück. In der Zwischenzeit hat es Feedback von Entwicklerinnen und Entwicklern gesammelt und die Anbindung entsprechend angepasst.

Um die Anbindung an ESM zu kennzeichnen, führt TypeScript 4.7 neue Einstellungen ein: Unter compilerOptions muss der Wert für module auf nodenext oder node16 stehen. Letzterer Wert war in der Beta zur aktuellen TypeScript-Version noch als node12 umgesetzt. Da Node.js 12 jedoch nicht mehr offiziell unterstützt wird, gilt für den Wert nun das im April 2021 erschienene Node.js 16, das gleichzeitig das jüngste LTS-Release (Long-term Support) ist.

Node.js kennt für die Datei package.json die Konfiguration "type". Dabei legt "module" das Zusammenspiel mit ESM fest, während "commonjs" für das ältere System steht. Erstere Einstellung erlaubt unter anderem import- und export-Anweisungen, und relative Importe benötigen anders als bei CJS die jeweilige Dateiendung wie ".js".

TypeScript untersucht für .ts-., .tsx-., .js-. oder .jsx-Dateien die zugehörige package.json, ob die Datei ein ES-Modul ist. Daraus ermittelt das System zum einen, wie es andere Module findet, die die Datei importiert. Beim Kompilieren von .ts-Dateien als ES-Modul fasst der Compiler zudem die import- und export-Anweisungen nicht an.

Für die explizite Angabe des Modulsystems im Namen kennt Node.js die Endungen .mjs für ES-Module und .cjs für CommonJS Module. Analog dazu führt TypeScript .mts und .cts an, die es beim Übertragen in JavaScript-Dateien in .mjs und .cjs umwandelt. Für Deklarationen gibt es zusätzlich die Dateiendungen .d.mts und .d.cts.

ECMAScrcipt-Module bringen grundlegende Änderungen beim Ausführen von JavaScript-Code mit sich. Unter anderem sind definierte Moduleintrittspunkte erforderlich. TypeScript versucht automatisch Dateien als Module zu identifizieren und sucht nach import- und export-Anweisungen.

Wer die Einordnung selbst steuern möchte, kann den Automatismus mit der im aktuellen Release eingeführten Konfiguration moduleDetection manuell überschreiben. "legacy" prüft weiterhin über import und export. "auto" ist die Standardeinstellung und testet zusätzlich unter anderem, ob das "type"-Feld in package.json auf "module" gesetzt ist. Unter "force" behandelt TypeScript jede Datei außer den Deklarationen als Modul, unabhängig von der Konfiguration über die Werte für module, moduleResoluton und jsx.

Jenseits der Integration der ECMAScript-Module bringt TypeScript 4.7 ein paar Ergänzungen unter der Haube mit. Unter anderem erweitert es die Kontrollflussanalyse für Elemente, auf die der Code über einen Index zugreift, wenn Letzterer ein Literal beziehungsweise ein eindeutiges Symbol ist. Damit erkennt TypeScript beispielsweise in folgendem Codeausschnitt, dass obj[key] innerhalb des if-Blocks ein String sein muss und somit die Methode toUpperCase() bietet:

if (typeof obj[key] === "string") {
    let str = obj[key].toUpperCase();
}

Im Zusammenspiel mit der Konfigurationseinstellung --strictPropertyInitialization erkennt TypeScript 4.7 zudem, wenn ein Element für den Zugriff über ein Literal definiert, aber nicht im Konstruktor initialisiert wurde, wie in folgendem Beispiel aus dem TypeScript-Blog:

// 'key' has type 'unique symbol'
const key = Symbol();

class C {
    [key]: string;

    constructor(str: string) {
        // oops, forgot to set 'this[key]'
    }

    screamString() {
        return this[key].toUpperCase();
    }
}

Weitere Neuerungen in TypeScript 4.7 wie die direkte Zuweisung bestimmter Typen für Generics lassen sich dem TypeScript-Blog entnehmen. Das aktuelle Release sollte über NuGet zur Verfügung stehen, wo sich beim Schreiben dieser Meldung jedoch noch Version 4.6.4 fand. Alternativ lässt es sich mit npm install typescript installieren.

Siehe auch:

(rme)