Menu

TypeScript Namespace: was er ist und wann man ihn nutzt

Ein TypeScript-Namespace fasst Werte und Typen unter einem Namen zusammen und wird zu einem einfachen Objekt kompiliert. Syntax, wie Namespaces miteinander und mit Funktionen und Klassen verschmelzen, warum ES-Module sie abgelöst haben und wo sie dir noch begegnen: in Deklarationsdateien und bei globaler Augmentation.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Ein Namespace ist ein benannter Block, der Funktionen, Konstanten und Typen unter einem Namen zusammenfasst. Mit export markierte Member sind als Name.member erreichbar; der Rest bleibt privat im Block. Zur Laufzeit ist ein Namespace ein einfaches Objekt.

Wozu ein Namespace kompiliert wird

Namespaces gehören zu den wenigen TypeScript-Features, die Code erzeugen. Der Block oben wird zu einer Funktion, die ein Objekt befüllt:

var Geometry;
(function (Geometry) {
    const TAU = Math.PI * 2;
    function circumference(radius) {
        return TAU * radius;
    }
    Geometry.circumference = circumference;
    function area(radius) {
        return Math.PI * radius ** 2;
    }
    Geometry.area = area;
})(Geometry || (Geometry = {}));

Mit diesem Muster hat JavaScript-Code, bevor es Module gab, vermieden, jeden Namen in den globalen Scope zu legen. TAU ist eine lokale Variable der Funktion, deshalb kann anderer Code sie nicht sehen. Der Versuch, sie zu lesen, ist ein Fehler beim Kompilieren:

Der Compiler meldet index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. Setze export vor const rate, und beide Zeilen laufen.

Verschachtelung, Merging und Aliasse

Namespaces lassen sich verschachteln, und zwei Blöcke mit demselben Namen verschmelzen zu einem. Das Geometry || (Geometry = {}) in der Ausgabe macht das möglich: Der zweite Block ergänzt das vorhandene Objekt. import X = A.B erzeugt einen kurzen Alias.

namespace A.B.C { } ist die Kurzform für drei verschachtelte Blöcke. Die alte Schreibweise module Shop { } bedeutet dasselbe, wird heute aber mit dem Fehler TS1540 abgelehnt, A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.

Merging mit Funktionen und Klassen

Ein Namespace kann seinen Namen mit einer Funktion, Klasse oder einem Enum teilen und ihm Member hinzufügen. Das ist immer noch der sauberste Weg, eine Funktion zu beschreiben, die auch Eigenschaften trägt, oder eine Klasse mit angehängten Hilfsfunktionen.

Bei einer Klasse erledigt eine static-Methode dieselbe Aufgabe und ist reines JavaScript. Bei einer Funktion kannst du den Namespace auch weglassen und direkt nach der Deklaration format.prefix = "$" zuweisen; TypeScript erfasst Eigenschaften, die so zugewiesen werden.

Namespaces vs Module

Vor den ES-Modulen bestand ein großes TypeScript-Programm aus vielen Skriptdateien mit gemeinsamen globalen Namespaces, die mit /// <reference path="..." /> verbunden und mit outFile zu einer Datei kompiliert wurden. Module haben das abgelöst: Jede Datei ist ein eigener Scope, Abhängigkeiten sind explizite Imports, und Bundler können ungenutzte Exports entfernen. TypeScript 7 hat outFile entfernt (Fehler TS5102), das Setup mit Namespaces über mehrere Dateien ist also keine Build-Option mehr.

NamespaceModul
Einheitein benannter Block in einer Dateidie Datei selbst
Scopeglobal, außer innerhalb eines Modulsimmer ein eigener
Abhängigkeitenimplizit, über die Ladereihenfolgeexplizites import
Ausgabeein Objekt, das eine Funktion aufbautimport/export oder require
Entfernen ungenutzten CodesBundler behalten jeden MemberBundler können ungenutzte Exports entfernen
Läuft mit Type Stripping in Nodenur wenn er ausschließlich Typen enthältja

Innerhalb eines Moduls fügt es ohne Nutzen eine zweite Namensebene hinzu, alles in einen Namespace zu packen: Importierende müssten Utils.Utils.format schreiben. Exportiere die Funktionen direkt und lass den Importierenden import * as Utils from "./utils.js" wählen, wenn er ein Präfix möchte.

Wo dir Namespaces noch begegnen

Deklarationsdateien nutzen sie, um Bibliotheken zu beschreiben, die ein einziges globales Objekt bereitstellen, und um Typen zu gruppieren:

// jquery-like.d.ts: a global function that also has properties
declare function $(selector: string): unknown;
declare namespace $ {
    const version: string;
    function ajax(url: string): Promise<unknown>;
}

Typpakete nutzen sie, um Typen bereitzustellen, die du erweitern kannst. @types/node deklariert namespace NodeJS, und um sein Interface ProcessEnv aus einem beliebigen Modul heraus zu ergänzen, brauchst du declare global:

// env.d.ts
export {};

declare global {
    namespace NodeJS {
        interface ProcessEnv {
            API_URL: string; // process.env.API_URL is now string, not string | undefined
        }
    }
}

Auch Interfaces in verschmolzenen Namespaces verschmelzen, und genau das lässt diese Augmentation funktionieren.

Namespaces und Type Stripping

Node 24 führt .ts-Dateien aus, indem es die Typsyntax löscht. Ein Namespace mit Werten lässt sich nicht löschen, er muss zu dem oben gezeigten Objekt kompiliert werden, also bricht node app.ts ab mit:

SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode

Zwei Arten von Namespaces sind unproblematisch, weil sie vollständig verschwinden: ein declare namespace und ein Namespace, dessen Member alle Typen oder Interfaces sind. Die Compileroption erasableSyntaxOnly: true meldet die übrigen beim Kompilieren als Fehler TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., und so halten Projekte, die auf Type Stripping laufen, sie fern. node --experimental-transform-types kompiliert Namespaces, mit einer Warnung, dass das Feature experimentell ist.

Häufig gestellte Fragen

Sollte ich in TypeScript Namespaces oder Module verwenden?

Nimm für neuen Code Module (import und export). Jede Datei ist bereits ein eigener Scope, Bundler und Node verstehen Module, und ungenutzte Exports können entfernt werden. Namespaces bleiben nützlich in Deklarationsdateien, für globale Augmentation und um einer gleichnamigen Funktion oder Klasse Typen oder Hilfsfunktionen anzuhängen.

Wozu wird ein TypeScript-Namespace kompiliert?

Zu einem Objekt, das eine sofort aufgerufene Funktion befüllt: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. Exportierte Member werden Eigenschaften dieses Objekts; Member ohne export bleiben lokal in der Funktion.

Was ist der Unterschied zwischen namespace und module in TypeScript?

Ein Modul ist eine Datei mit import oder export auf oberster Ebene. Ein Namespace ist ein benannter Block innerhalb einer Datei. Früher nannte TypeScript Namespaces "internal modules" und erlaubte module Foo {}; diese Schreibweise ist heute der Fehler TS1540, und nur namespace Foo {} wird akzeptiert.

Kann Node TypeScript-Dateien mit Namespaces ausführen?

Nicht mit seinem standardmäßigen Type Stripping. Ein Namespace mit Werten erzeugt ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, weil Stripping nur Typen löscht und ein Namespace erzeugten Code braucht. Namespaces, die nur Typen enthalten, und declare namespace werden entfernt und laufen problemlos.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S