Programmiersprache: TypeScript 4.5 jongliert mit Node.js-Modulen

Um im Zusammenspiel mit Node.js neben CommonJS- auch ECMAScript-Module zu verarbeiten, kennt TypeScript neue Einstellungen und Dateinamenserweiterungen.

In Pocket speichern vorlesen Druckansicht
Schreibmaschine, Tastatur, Keyboard, Digitalisierung

(Bild: , gemeinfrei)

Lesezeit: 4 Min.
Inhaltsverzeichnis

Microsoft hat die erste Beta von TypeScript 4.5 veröffentlicht. Das Release bringt Anpassungen für das Zusammenspiel mit ECMAScript-Modulen in Node.js mit. Außerdem entfernt es in einigen Fällen endrekursive Funktionsaufrufe und greift zwei geplante Neuerungen im ECMAScript-Standard auf.

Node.js kann neben dem traditionell genutzten Modulsystem CommonJS (CJS) inzwischen auch ECMAScript Modules (ESM) verarbeiten. Für das Zusammenspiel führt TypeScript 4.5 unter den compilerOptions die module-Einstellungen node12 und nodenext ein.

Die neuen Modulmodi überprüfen, ob es sich bei einer .ts-., .tsx-., .js-. oder .jsx-Datei um ein ES-Modul handelt. In dem Fall verarbeitet das System Importe weiterer Module nach den Richtlinien für ESM. So müssen unter anderem relative Importpfade die Dateinamenserweiterung enthalten. Beispielsweise muss die Angabe import "./foo.js" lauten, während bei CJS import "./foo") ohne Endung erlaubt ist.

Die Art der Module lässt sich grundsätzlich in Node.js zum einen in package.json über den Eintrag "type": "module" oder "type": "commonjs" als ESM beziehungsweise CJS-Modul deklarieren. Daneben erlaubt Node.js die explizite Festlegung über die Dateiendung über .mjs für EMS und .cjs für CJS. Analog dazu führt TypeScript 4.5 die Endungen .mts und .cts ein.

Zwei Änderungen betreffen die Optimierungen von TypeScript unter der Haube. So versucht das System zu erkennen, ob eine Rekursion potenziell unendlich läuft oder eine Typexpansion sehr lange benötigt. Einige Typen können fälschlicherweise die Heuristik auslösen, die TypeScript zum Erkennen nutzt. Als Beispiel führt der Blogbeitrag einen Codeausschnitt auf, der führende Leerzeichen aus String-ähnlichen Typen entfernt:

type TrimLeft<T extends string> =
    T extends ` ${infer Rest}` ? TrimLeft<Rest> : T;

// Test = "hello" | "world"
type Test = TrimLeft<"   hello" | " world">;

Für den Beispielcode funktioniert die Umsetzung. Sollte eine Zeichenkette aber sehr viele Leerzeichen am Anfang haben, führt die heuristische Erkennung zu der Fehlermeldung

error: Type instantiation is excessively deep
and possibly infinite.

In dem konkreten Anwendungsfall ist der Overhead jedoch weniger kritisch, da die Funktion endrekursiv in einem Zweig ist und direkt das Ergebnis zurückgibt, ohne es weiter zu verarbeiten. TypeScript 4.5 entfernt neuerdings die endständigen Funktionsaufrufe, wenn ein Zweig des konditionalen Typs selbst wieder ein Conditional Type ist. Auf die Weise sind keine zusätzlichen Instanziierungen erforderlich, die den Speicherbedarf der rekursiven Funktion potenziell ins Unendliche treiben.

TypeScript 4.5 führt zudem den neuen Parameter --preserveValueImports ein, der das automatische Entfernen von scheinbar nicht genutzten Importen verhindert. Als Beispiel greift der Blogbeitrag auf einen Codeschnipsel mit eval zurück:

import { Animal } from "./animal.js";

eval("console.log(new Animal().isDangerous())");

Auch wenn grundsätzlich der Einsatz von eval in TypeScript und JavaScript schon aus Sicherheitsgründen fragwürdig ist, demonstriert das Beispiel einen Fall, bei dem TypeScript womöglich einen Import zu Unrecht als überflüssig ansieht und entfernt, da das JavaScript-Modul innerhalb der Zeichenkette verwendet wird. Ähnliches gilt für das Zusammenspiel mit Frameworks wie Vue.js oder Svelte. Dort sind einige Importe für das Build-System nicht auf Anhieb als genutzt erkennbar, weil sie in script-Passagen eingebunden sind.

Mit zwei Neuerungen greift TypeScript ECMAScript-Vorschläge auf, die noch nicht im Standard existieren, aber geplant sind. Zum einen kann TypeScript überprüfen, ob ein Objekt ein privates Feld enthält, das in der aktuellen Klasse definiert ist:

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

    equals(other: unknown) {
        return other &&
            typeof other === "object" &&
            #name in other && // <- this is new!
            this.#name === other.#name;
    }
}

Das zugehörige ECMA-Proposal trägt den Titel "Ergonomic brand checks for Private Fields" (in etwa übersetzbar als "Ergonomische Sortenüberprüfung für private Felder"). Über die privaten Felder lässt sich prüfen, ob es sich tatsächlich um eine Instanz der Klasse handelt, da sie nur in dem Fall existieren.

Außerdem lassen sich die ebenfalls für ECMAScript vorgesehenen Import Assertions in TypeScript 4.5 nutzen. Sie stellen sicher, dass eine importierte Datei ein bestimmtes Format hat.

import obj from "./something.json" assert { type: "json" };

Allerdings kümmert sich das Build-System nicht um die tatsächliche Überprüfung des Formats der Inhalte, da die Verantwortung dafür beim Browser beziehungsweise bei der Runtime liegt.

Weitere Neuerungen in TypeScript 4.5 lassen sich dem Entwicklungsblog bei Microsoft entnehmen. Die Betaphase von TypeScript 4.5 ist auf gut sechs Wochen ausgelegt, und das endgültige Release soll laut Roadmap am 16. November 2021 erscheinen.

(rme)