warum Single-File-Transpiler dieses Flag erzwingen müssen
esbuild, SWC und Babel kompilieren jede Datei für sich allein, ohne Typinformationen aus anderen Dateien. isolatedModules deckt genau die Fälle auf, die dabei scheitern würden.
Inhaltsverzeichnis
- 1. Was isolatedModules technisch bedeutet
- 2. Warum moderne Build-Tools Dateien einzeln transpilieren
- 3. Re-Exports von Typen: export type erzwingen
- 4. const enum: warum es mit isolatedModules nicht funktioniert
- 5. Namespace-Merging und andere verbotene Patterns
- 6. isolatedModules vs. verbatimModuleSyntax: die Beziehung
- 7. Typische Fehlermeldungen und wie man sie behebt
- 8. Konfiguration in Vite-, esbuild- und SWC-Projekten
- 9. Checkliste zur Migration eines Bestandsprojekts
- 10. Zusammenfassung
- 11. FAQ
1. Was isolatedModules technisch bedeutet
isolatedModules ist selbst kein Transpiler-Flag, sondern ein reines Type-Checking-Flag: es ändert nichts an der Ausgabe von tsc, sondern meldet einen Fehler, sobald Code geschrieben wird, der sich nicht korrekt kompilieren ließe, wenn jede Datei isoliert, ohne Wissen über andere Dateien, verarbeitet würde.
Der Hintergrund: tsc selbst braucht diese Einschränkung nicht, weil es beim Kompilieren immer das vollständige Programm mit allen Typinformationen sieht. Andere Tools tun das nicht, und genau für diese Fälle ist das Flag gedacht.
Aktiviert man isolatedModules, simuliert tsc quasi die Beschränkungen eines Single-File-Transpilers und warnt frühzeitig vor Code, der zwar in der eigenen IDE fehlerfrei aussieht, im Build aber kaputtgehen würde.
2. Warum moderne Build-Tools Dateien einzeln transpilieren
Tools wie esbuild, SWC oder der Babel-TypeScript-Preset entfernen Typannotationen rein syntaktisch, ohne den Rest des Projekts zu analysieren. Das ist der Grund für ihre enorme Geschwindigkeit: keine Typauflösung, kein Cross-File-Wissen, nur Textersetzung basierend auf lokaler Syntax.
Diese Geschwindigkeit hat einen Preis: der Transpiler kann nicht wissen, ob ein importiertes Symbol ein Typ oder ein Wert ist, wenn diese Information nicht direkt an der Importstelle sichtbar ist. Bei tsc selbst ist das kein Problem, weil der volle Typgraph bekannt ist.
Genau deshalb ist die Kombination aus tsc für reine Typprüfung (mit noEmit) und einem separaten schnellen Transpiler für die eigentliche JavaScript-Ausgabe inzwischen der Standard-Workflow in Vite, esbuild-basierten Setups und modernen Monorepo-Pipelines.
3. Re-Exports von Typen: export type erzwingen
Ein klassischer Fall, den isolatedModules abfängt, ist der Re-Export eines reinen Typs über ein normales export { Foo }, wenn Foo ausschließlich ein Interface oder ein Typalias ist. Ein Single-File-Transpiler kann ohne Typinformation nicht wissen, dass diese Zeile zur Laufzeit komplett entfernt werden muss.
Die Lösung ist export type { Foo }, eine explizite Syntax, die dem Transpiler bereits auf Textebene mitteilt, dass hier nichts zur Laufzeit existiert und die Zeile beim Kompilieren vollständig verschwinden darf.
Seit TypeScript 5.0 erkennt isolatedModules auch gemischte Exports korrekt, bei denen ein Modul sowohl Werte als auch Typen unter demselben Bezeichner exportiert, was vorher zu verwirrenden False Positives führen konnte.
// types.ts
export interface User { id: string; name: string }
export const DEFAULT_ROLE = "guest";
// index.ts: ohne isolatedModules würde das kompilieren, aber
// ein Single-File-Transpiler wüsste nicht, dass User rein type-only ist:
export { User, DEFAULT_ROLE }; // Fehler mit isolatedModules
// Korrekt:
export type { User };
export { DEFAULT_ROLE };
4. const enum: warum es mit isolatedModules nicht funktioniert
const enum ist eine reine Compile-Zeit-Optimierung: tsc ersetzt jede Nutzung durch den konkreten Wert (Inlining) und generiert kein Objekt zur Laufzeit. Das erfordert aber, dass der Compiler beim Kompilieren einer Datei die Definition des Enums aus einer anderen Datei kennt.
Ein Single-File-Transpiler sieht diese Definition nicht und kann das Inlining nicht durchführen, würde also entweder einen Laufzeitfehler produzieren oder den Enum-Zugriff unverändert stehen lassen, was zur Laufzeit auf ein nicht existierendes Objekt zeigt.
Mit aktiviertem isolatedModules meldet TypeScript jede Nutzung von const enum außerhalb der eigenen Datei als Fehler. Die übliche Lösung ist ein normales enum oder ein Objektliteral mit as const, das denselben Typkomfort ohne das Inlining-Problem bietet.
// Problematisch mit isolatedModules:
export const enum Status { Active, Archived }
// Robuste Alternative, funktioniert überall gleich:
export const Status = { Active: "active", Archived: "archived" } as const;
export type Status = (typeof Status)[keyof typeof Status];
5. Namespace-Merging und andere verbotene Patterns
Auch namespace-Deklarationen, die sich über mehrere Dateien hinweg zu einem gemeinsamen Namespace zusammenfügen (Declaration Merging), verlangen Wissen über die gesamte Codebasis und werden unter isolatedModules eingeschränkt, sofern sie nicht ausschließlich Typen enthalten.
Ein weiterer Sonderfall betrifft Dateien ganz ohne import oder export: TypeScript behandelt sie standardmäßig als globale Skripte. Unter isolatedModules kann das zu Warnungen führen, weil ein Single-File-Transpiler nicht zuverlässig zwischen einem Modul ohne Exporte und einem echten globalen Skript unterscheiden kann.
In der Praxis betreffen diese Randfälle deutlich seltener echten Anwendungscode als das export type-Problem, sind aber bei der Migration alter Codebasen mit historisch gewachsenen Namespace-Strukturen oft die aufwendigsten Stellen.
6. isolatedModules vs. verbatimModuleSyntax: die Beziehung
verbatimModuleSyntax, eingeführt in TypeScript 5.0, geht über isolatedModules hinaus: während isolatedModules nur verhindert, dass Code entsteht, der bei Single-File-Transpilation kaputtgehen würde, erzwingt verbatimModuleSyntax zusätzlich, dass Import- und Export-Anweisungen exakt so im Output erscheinen, wie sie geschrieben wurden.
In der Praxis aktivieren die meisten modernen Projekte inzwischen beide Flags gemeinsam, weil verbatimModuleSyntax die Regeln von isolatedModules im Kern mit abdeckt und zusätzliche Klarheit über das Modulsystem erzwingt.
Wer heute ein neues Projekt aufsetzt, sollte direkt zu verbatimModuleSyntax greifen, isolatedModules bleibt aber für bestehende Projekte und ältere TypeScript-Versionen relevant, die dieses neuere Flag noch nicht unterstützen.
7. Typische Fehlermeldungen und wie man sie behebt
Die Fehlermeldung Re-exporting a type when isolatedModules is enabled requires using export type ist die häufigste und wird durch Hinzufügen des type-Modifiers am Export gelöst, entweder pro Symbol oder für die gesamte Export-Anweisung.
Bei const enum-Fehlern lautet die Meldung sinngemäß, dass const enums nicht über Modulgrenzen hinweg genutzt werden können. Hier hilft nur die Umstellung auf ein normales Enum oder ein Objektliteral mit as const, ein Refactoring lohnt sich meist projektweit statt punktuell.
Wichtig ist, diese Fehler nicht pauschal mit einem // @ts-ignore zu unterdrücken, weil das Problem in genau den Fällen real ist, in denen es der Compiler meldet: der Build-Tool-Output wäre tatsächlich fehlerhaft, nicht nur der Type-Checker übervorsichtig.
8. Konfiguration in Vite-, esbuild- und SWC-Projekten
In Vite-Projekten ist isolatedModules: true seit mehreren Major-Versionen die empfohlene Standardeinstellung in der tsconfig, weil Vite intern esbuild für die TypeScript-Transformation nutzt und somit exakt die Single-File-Beschränkungen hat, vor denen das Flag warnt.
Bei SWC-basierten Setups, etwa in Next.js, gilt dieselbe Logik: SWC transpiliert Dateien ebenfalls isoliert, weshalb isolatedModules in der zugrunde liegenden tsconfig aktiviert sein sollte, auch wenn SWC selbst das Flag nicht direkt liest.
Für reine tsc-Only-Projekte ohne separaten schnellen Transpiler ist isolatedModules optional, schadet aber nicht: es zwingt lediglich zu saubererem Code, der auch bei einem späteren Wechsel des Build-Tools ohne Überraschungen funktioniert.
9. Checkliste zur Migration eines Bestandsprojekts
Vor der Aktivierung lohnt sich ein Testlauf: isolatedModules in der tsconfig setzen und tsc --noEmit ausführen, um alle betroffenen Stellen als Fehlerliste zu erhalten, bevor man tatsächlich auf einen anderen Transpiler wechselt.
Die folgende Tabelle fasst die häufigsten verbotenen Patterns und ihre Lösung zusammen.
| Pattern | Problem unter isolatedModules | Lösung | Betroffen seit |
|---|---|---|---|
| export { Foo } für reinen Typ | Transpiler kennt Typ-Status nicht | export type { Foo } | TS 3.8+ |
| const enum über Dateigrenzen | Inlining nicht möglich ohne Kontext | enum oder as const-Objekt | TS 3.8+ |
| namespace mit Merging über Dateien | Braucht projektweites Wissen | Module statt Namespace nutzen | TS 3.8+ |
| Datei ohne import/export | Mehrdeutig: Modul oder globales Skript | Mindestens einen export hinzufügen | TS 3.8+ |
Mironsoft
TypeScript-Migration, Typsicherheit und Team-Onboarding
JavaScript-Codebasis ohne Typsicherheit, aber keine Zeit für eine Rundum-Migration?
Wir migrieren bestehende JavaScript-Projekte schrittweise zu TypeScript, richten strikte Compiler-Einstellungen sauber ein und bringen Teams mit Code-Reviews und Style-Guides auf denselben Typsicherheits-Stand.
Migrations-Fahrplan
Schrittweise JS-zu-TS-Migration ohne Big-Bang-Risiko planen und umsetzen.
Strict-Mode-Einführung
tsconfig.json, ESLint-Regeln und CI-Checks für dauerhafte Typsicherheit aufsetzen.
Team-Onboarding
Entwickler mit Workshops und Code-Reviews in TypeScript-Best-Practices einarbeiten.
10. Zusammenfassung
isolatedModules
Art des Flags
Reines Type-Checking-Flag, ändert nichts an der tsc-Ausgabe selbst.
Zielgruppe
Projekte mit esbuild, SWC, Babel oder Vite als Transpiler.
Häufigster Fehler
export type fehlt bei Re-Export eines reinen Typs.
Nachfolger
verbatimModuleSyntax deckt dieselben Fälle plus mehr ab.