Der TypeScript-Fehler TS1384 gehört zu denen, die beim ersten Kontakt kryptisch wirken, aber immer eine präzise und behebbare Ursache haben. Er tritt auf, wenn der Compiler einen export-Modifizierer an einer Stelle findet, an der er nicht stehen darf – genauer gesagt innerhalb eines Modulerweiterungs-Blocks. Dieser Leitfaden erklärt genau, was TS1384 bedeutet, geht jedes auslösende Szenario durch und nennt für jedes die richtige Lösung.
Was ist TS1384?
Der TypeScript-Fehler TS1384 trägt die Meldung: „Der Modifizierer 'export' kann nicht auf eine Modulerweiterung angewendet werden." Er tritt auf, wenn der Compiler das Schlüsselwort `export` in einem `declare module`- oder `declare global`-Block findet – den beiden Konstrukten, die TypeScript für Modulerweiterungen nutzt. Erweiterungsblöcke dienen dem Erweitern bestehender Modultypen, nicht dem Deklarieren neuer öffentlicher Symbole; `export` ist darin daher strukturell ungültig.
Modulerweiterung in einem Satz
Modulerweiterung ist der TypeScript-Mechanismus, mit dem du neuen Mitglieder zu den Typen eines bestehenden Moduls hinzufügen kannst – zum Beispiel eine eigene Eigenschaft zu `Express.Request`, eine Erweiterung von `Window` um ein Fremd-Global oder zusätzliche Methoden in den Komponentenoptionen eines Frameworks. Die Syntax sieht wie ein `declare module 'modulname' {}`-Block aus und muss in einer Moduldatei stehen (einer Datei mit mindestens einer `import`- oder `export`-Anweisung auf oberster Ebene).
- TS1384 ist ein Compilerfehler – die Datei wird nicht typgeprüft, bis er behoben ist
- Er betrifft die Laufzeit nicht – der Fehler betrifft ausschließlich Typdeklarationen
- Er ist deterministisch – derselbe Code löst ihn immer aus; es gibt keine sporadische Variante
- Er hat wenige Ursachen – Skript- vs. Modulkontext, isolatedModules und falsche .d.ts-Struktur decken 95 % der Fälle ab
Note
Wann TS1384 auftritt
Der Fehler betrifft immer ein `export` an einer Stelle, an der TypeScript es nicht zulässt. Es gibt drei verschiedene Muster, die ihn erzeugen; zu erkennen, welches auf deine Codebasis zutrifft, bestimmt den richtigen Fix.
Muster 1 – export in einem declare-module-Block
Der direkteste Auslöser: Du schreibst eine `export`-Anweisung in eine `declare module`-Erweiterung. Die Absicht ist meist, etwas zur öffentlichen API des Moduls hinzuzufügen, aber Erweiterungsblöcke funktionieren nicht so – sie können nur Typdeklarationen erweitern, die im Zielmodul bereits existieren.
// ✗ TS1384 - export innerhalb einer Modulerweiterung
declare module 'some-library' {
export interface NewInterface { // <-- hier wird TS1384 ausgelöst
id: string;
}
}
// ✓ Korrekt - Interface ohne export hinzugefügt
declare module 'some-library' {
interface ExistingInterface {
newProperty: string; // erweitert den vorhandenen Typ
}
}Muster 2 – die Datei gilt als Skript, nicht als Modul
Das ist die häufigste Ursache für TS1384 in echten Projekten. Hat eine Datei keine `import`- oder `export`-Anweisungen auf oberster Ebene, behandelt TypeScript sie als Skript mit globalem Gültigkeitsbereich. Ein `declare global {}`-Block in einer Skriptdatei ist sinnlos – in Skripten ist bereits alles global –, deshalb lehnt TypeScript jedes `export` darin mit TS1384 ab. Die Datei muss ein Modul sein, damit der Erweiterungskontext Sinn ergibt.
Muster 3 – falsche Struktur der .d.ts-Datei
Deklarationsdateien (`.d.ts`), die Ambient-Moduldeklarationen und reguläre export-Anweisungen in der falschen Reihenfolge mischen, können TS1384 auslösen. Eine `.d.ts`, die mit `export`-Deklarationen auf oberster Ebene beginnt und danach einen `declare module`-Block enthält, gilt als Modul – das ist korrekt. Eine `.d.ts` dagegen, die alles in einen einzigen `declare module`-Block packt und dann innerhalb dieses Blocks `export` verwendet, verwechselt den Erweiterungskontext mit einem Moduldefinitionskontext.
Warning
TS1384 beheben: Modulerweiterung
Die richtige Lösung hängt davon ab, was du eigentlich erreichen willst. Zwei unterschiedliche Ziele können zu TS1384 führen und verlangen unterschiedliche Vorgehensweisen. Diese vier Schritte lösen den Fehler sauber auf.
Ermittle, welches Muster TS1384 auslöst
Lies die vollständige Compiler-Meldung sorgfältig. Achte auf die Dateiendung (.ts oder .d.ts), die Zeilennummer und darauf, ob das `export` in einem `declare module`-Block, einem `declare global`-Block oder woanders steht. Der umgebende Codekontext verrät dir, welches der drei Muster oben zutrifft. Beginnt der Fehlerpfad mit `node_modules/`, springe direkt zum `skipLibCheck`-Fix – die anderen Muster greifen dort nicht.
export {} hinzufügen, um die Datei in ein Modul umzuwandeln
Enthält deine Datei einen `declare module`- oder `declare global`-Block, aber keine Imports oder Exports auf oberster Ebene, füge oben `export {}` hinzu. Diese eine Zeile wandelt die Datei vom Skript- in den Modulkontext um – die Voraussetzung für die Erweiterungssyntax. Der leere Export fügt der kompilierten Ausgabe nichts hinzu; er ist reines Kontextsignal für TypeScript.
// Diese Zeile macht die Datei zu einem Modul
export {};
declare global {
interface Window {
myAnalytics: AnalyticsInstance;
}
}declare global in ein vorhandenes Modul verschieben
Eine Alternative zu `export {}` ist, den `declare global`-Block in eine Datei zu legen, die bereits echte Imports oder Exports hat – etwa den Einstiegspunkt einer Bibliothek, ein Feature-Modul oder eine gemeinsame Utility-Datei. So bleiben deine Typ-Erweiterungen beim Code, den sie erweitern, was leichter zu pflegen sein kann als eine eigene Datei für globale Deklarationen.
export aus dem Erweiterungsblock entfernen
Wenn du tatsächlich einen neuen exportierbaren Typ zur öffentlichen API eines bestehenden Moduls hinzufügen willst, ist der Erweiterungsblock der falsche Ort. Verschiebe die Deklaration vollständig aus dem `declare module`-Block heraus. Um ein vorhandenes Interface zu erweitern, verwende denselben Interface-Namen ohne `export` innerhalb des Blocks – TypeScript führt ihn über Declaration Merging automatisch zusammen.
JSON-Formatierer und -Validator
Prüfe deine tsconfig.json und package.json sofort im Browser auf Syntaxfehler – ganz ohne TypeScript-Compiler.
TS1384 und isolatedModules
Die Compiler-Option `isolatedModules` ist standardmäßig in Vite, Next.js, Create React App mit Babel und jedem Projekt mit esbuild oder SWC aktiv. Sie verlangt, dass jede Datei ohne dateiübergreifende Typinformationen unabhängig transformierbar ist, was Einschränkungen schafft, die mehrere TypeScript-Fehler verstärken – darunter TS1384.
Was isolatedModules einschränkt
| Konstrukt | Ohne isolatedModules | Mit isolatedModules |
|---|---|---|
| `const enum` | ✓ Überall erlaubt | ✗ Nur in .d.ts-Dateien |
| `export type` | ✓ Optional | ✓ Pflicht für reine Typ-Re-Exports |
| `import type` | ✓ Optional | ✓ Pflicht für reine Typ-Imports |
| Ambient-Deklarationen | ✓ In jeder .ts-Datei | ✓ Besser in .d.ts-Dateien |
| Modulerweiterung | ✓ In Modul-.ts-Dateien | ✓ Bevorzugt in .d.ts-Dateien |
| Namespace-Re-Exports | ✓ Erlaubt | ✗ Eingeschränkt |
Der spezifische Fix für isolatedModules
Wenn `isolatedModules` aktiv ist und TS1384 bei einer Erweiterung in einer regulären `.ts`-Datei auftritt, ist der sauberste Fix, die Erweiterung in eine `.d.ts`-Datei zu verschieben. Deklarationsdateien werden nie von esbuild oder Babel transformiert – sie liest nur der TypeScript-Compiler. Damit entfällt die isolatedModules-Einschränkung für diese Erweiterung vollständig.
// Dieses Muster funktioniert korrekt mit isolatedModules
export {};
declare module 'express' {
interface Request {
userId?: string;
tenantId?: string;
}
}Tip
TS1384 in .d.ts-Dateien
Deklarationsdateien bringen eigene Feinheiten in TS1384. Die Regeln dafür, was in einer `.d.ts`-Datei gültig ist, unterscheiden sich leicht von regulären `.ts`-Dateien, und die Muster, die TS1384 in Deklarationsdateien erzeugen, sind oft weniger intuitiv.
Ambient-Moduldefinition oder Erweiterung?
Eine `.d.ts`-Datei kann zwei grundlegend verschiedene Dinge enthalten, die ähnlich aussehen, sich aber anders verhalten. Eine Ambient-Moduldefinition (`declare module 'name' {}` in einer `.d.ts` im Skriptkontext, ohne Imports oder Exports) definiert alle Typen eines Moduls von Grund auf – sie wird verwendet, um untypisierte JavaScript-Bibliotheken zu typisieren. Eine Modulerweiterung (`declare module 'name' {}` in einer `.d.ts` im Modulkontext mit `export {}`) erweitert die Typen eines bestehenden Moduls. Die Unterscheidung ist wichtig, weil `export` in einer Ambient-Moduldefinition gültig ist, in einer Modulerweiterung jedoch TS1384 erzeugt.
Das richtige .d.ts-Muster wählen
Wenn deine `.d.ts`-Datei Typen für eine untypisierte Bibliothek definiert (z. B. ein altes jQuery-Plugin typisiert), lass sie im Skriptkontext ohne `export {}` am Anfang. Verwende `export` im `declare module`-Block frei. Wenn deine `.d.ts`-Datei eine bereits typisierte Bibliothek erweitert (z. B. eine Eigenschaft zu `Express.Request` hinzufügt), füge oben `export {}` hinzu und entferne jedes `export` aus dem `declare module`-Block.
Note
skipLibCheck als letztes Mittel
Wenn TS1384 auf einem Pfad innerhalb von `node_modules` auftritt und du das Paket nicht kontrollierst, füge `"skipLibCheck": true` in deine tsconfig.json ein. Das weist TypeScript an, die Typprüfung aller `.d.ts`-Dateien zu überspringen – auch derer in `node_modules`. Es ist eine legitime, weit verbreitete Konfigurationsoption, kein Hack. Der Preis: Du verlierst die Typprüfung der Bibliotheksdeklarationen vollständig, sodass wirklich kaputte Typen in Abhängigkeiten unbemerkt bleiben. Nutze `skipLibCheck` nur, wenn die Alternative darin besteht, deinen Build zu blockieren.
TS1384 in Frameworks und Bundlern
Jedes Framework und Build-Tool konfiguriert TypeScript anders, weshalb TS1384 je nach Stack aus leicht unterschiedlichen Gründen auftreten kann. So entsteht und verschwindet der Fehler in den gängigsten Umgebungen.
Next.js
Next.js aktiviert `isolatedModules` über seine Standard-tsconfig automatisch und nutzt SWC für die Transformation. Das Standardmuster für Typerweiterungen in Next.js ist ein eigenes `types/`-Verzeichnis im Projektstamm mit `.d.ts`-Dateien, die mit `export {}` beginnen. Next.js erzeugt außerdem eine `next-env.d.ts` – bearbeite diese Datei nie manuell, da sie bei jedem Build neu erzeugt wird und deine Änderungen verloren gehen. Lege deine Erweiterungen in einer separaten Datei ab.
Vite
Vite-Projekte nutzen esbuild für die Transformation und enthalten `isolatedModules: true` in der Standard-tsconfig. Vite erzeugt zudem eine `vite-env.d.ts` für eigene Globals. Lege Modulerweiterungen in einem separaten `src/types/`-Verzeichnis ab. Das `export {}`-Muster behebt TS1384 in allen Standard-Vite-Setups, und Erweiterungen in `.d.ts`-Dateien zu halten ist der sicherste Ansatz, wenn die esbuild-Transformation in der Kette liegt.
Reine Node.js-/Express-APIs
Express-Projekte erweitern häufig `Express.Request`, um Nutzersitzung oder Auth-Kontext hinzuzufügen. Das kanonische Muster ist eine Datei `src/types/express/index.d.ts` mit `export {}` am Anfang, gefolgt von der Erweiterung `declare module 'express-serve-static-core'`. Ohne `isolatedModules` funktioniert das auch als reguläre `.ts`-Datei – die `.d.ts`-Variante ist jedoch in jedem Fall die sauberere Konvention. Prüfe immer den exakten Modulnamen in den Express-Typdefinitionen, denn das Erweiterungsziel ist `express-serve-static-core`, nicht `express`.
Die Datei muss ein Modul sein, bevor sie eines erweitern kann. Ein einziges `export {}` macht aus einem Skript ein Modul – und schaltet das gesamte Erweiterungssystem frei.
TS1384 langfristig vermeiden
TS1384 entsteht leicht versehentlich, besonders wenn neue Teammitglieder Typerweiterungen hinzufügen oder du ein Projekt auf einen neuen Bundler umstellst. Diese Praktiken verhindern, dass er nach der Behebung zurückkehrt.
Eine Konvention für das Types-Verzeichnis etablieren
Lege ein Verzeichnis `src/types/` (oder `types/`) an und bewahre dort alle Modulerweiterungen als `.d.ts`-Dateien auf. Jede Datei sollte eine Erweiterung enthalten und mit `export {}` beginnen. Dokumentiere diese Konvention im `CONTRIBUTING.md` deines Projekts, damit neue Beitragende wissen, wohin Typdeklarationen gehören. Ein einheitlicher Ort erleichtert zudem die Prüfung der Erweiterungen bei Abhängigkeits-Upgrades.
tsc --noEmit in der CI nutzen
Ein CI-Schritt mit `tsc --noEmit` erkennt TS1384 (und jeden anderen TypeScript-Fehler), bevor Code den main-Branch erreicht. Viele Projekte überspringen die Typprüfung in der CI, weil ihr Bundler sie nicht verlangt – esbuild und SWC entfernen Typen, ohne sie zu prüfen. Füge `tsc --noEmit` als separaten Job-Schritt hinzu, damit Typfehler einschließlich TS1384 bei jedem Pull Request auffallen.
tsconfig-Änderungen mit dem Diff-Viewer prüfen
Änderungen an der tsconfig.json – etwa `isolatedModules` hinzufügen, `moduleResolution` umstellen oder `lib` aktualisieren – können TS1384 in Dateien einführen, die zuvor sauber kompilierten. Wenn eine tsconfig-Änderung ansteht, nutze den Diff-Viewer, um alte und neue Konfiguration nebeneinander zu vergleichen. So siehst du sofort, welche Optionen sich geändert haben, und kannst neue TS1384-Fehler auf die konkrete Einstellung zurückführen.
Tip
Diff-Viewer
Vergleiche zwei Versionen von tsconfig.json, package.json oder beliebigen Textdateien nebeneinander, um genau zu sehen, was sich geändert hat – im Browser, ohne Upload.
Key takeaways
- TS1384 tritt auf, wenn ein `export`-Modifizierer in einem `declare module`- oder `declare global`-Erweiterungsblock steht – der Fix ist fast immer eine einzige Zeile.
- Häufigste Ursache: Die Datei hat keine Imports oder Exports, TypeScript behandelt sie also als Skript. Füge oben `export {}` hinzu, um sie in ein Modul umzuwandeln.
- Mit `isolatedModules: true` (Vite-, Next.js-, esbuild-Projekte) verschiebe Erweiterungen in `.d.ts`-Dateien – sie sind von der Transformation ausgenommen und umgehen die Einschränkung vollständig.
- Zeigt TS1384 auf einen Pfad in `node_modules`, ist `skipLibCheck: true` in der tsconfig.json der Fix – Deklarationsdateien von Abhängigkeiten kannst du nicht bearbeiten.
- Ambient-Moduldefinitionen (untypisierte Bibliotheken typisieren) und Modulerweiterungen (typisierte Bibliotheken erweitern) sehen ähnlich aus, verhalten sich aber anders: `export` im Block ist nur in Definitionen gültig, nicht in Erweiterungen.
- Nimm `tsc --noEmit` in deine CI-Pipeline auf, um TS1384 bei jedem Pull Request zu erkennen, auch wenn dein Bundler (esbuild, SWC) beim Build keine Typprüfung durchführt.
- Validiere die tsconfig.json-Syntax nach Änderungen mit dem JSON-Formatierer und -Validator – ein JSON-Syntaxfehler verhindert still, dass TypeScript deine Konfiguration liest.