Das isolatedModules-Flag erklärt: warum es bei modernen Build-Tools Pflicht ist
AI generated
type
TypeScript
isolatedModules
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.

8 Min. Lesezeit TypeScript 5.x Build-Tools

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.

11. FAQ: isolatedModules

1Ändert isolatedModules die Ausgabe von tsc?
Nein, es ist ein reines Diagnose-Flag. Es meldet Fehler bei Code, der bei Single-File-Transpilation scheitern würde, verändert aber selbst keine Zeile der generierten JavaScript-Ausgabe von tsc.
2Brauche ich isolatedModules, wenn ich nur tsc zum Kompilieren nutze?
Nicht zwingend, weil tsc immer das vollständige Programm kennt und die betroffenen Patterns korrekt kompilieren kann. Sinnvoll ist es trotzdem, falls später ein Wechsel zu einem schnelleren Transpiler geplant ist.
3Warum genau scheitert const enum bei Single-File-Transpilation?
const enum wird beim Kompilieren durch den konkreten Wert ersetzt, was Wissen über die Enum-Definition erfordert. Ein Single-File-Transpiler sieht bei der Verarbeitung einer importierenden Datei diese Definition nicht und kann das Inlining nicht durchführen.
4Ist isolatedModules dasselbe wie verbatimModuleSyntax?
Nein, verbatimModuleSyntax ist umfassender und wurde in TypeScript 5.0 eingeführt. Es deckt die Fälle von isolatedModules mit ab, erzwingt aber zusätzlich, dass Import- und Export-Syntax unverändert im Output erscheint.
5Kann ich isolatedModules-Fehler mit ts-ignore unterdrücken?
Technisch ja, davon wird aber abgeraten, weil das gemeldete Problem im jeweiligen Build-Tool tatsächlich zu fehlerhaftem oder fehlendem Code führen würde. Die korrekte Lösung ist fast immer eine kleine Syntaxanpassung.
6Wird isolatedModules in Vite standardmäßig aktiviert?
In von Vite generierten TypeScript-Projektvorlagen ist es in der tsconfig üblicherweise bereits voreingestellt, weil Vite intern esbuild für die Transformation nutzt und genau diese Beschränkungen hat.
7Betrifft isolatedModules auch reine JavaScript-Dateien in einem TypeScript-Projekt?
Nein, das Flag wirkt sich ausschließlich auf TypeScript-spezifische Konstrukte wie type-only Exports und const enum aus, die in reinem JavaScript gar nicht existieren.
8Muss ich bei jedem einzelnen Typ-Export type explizit schreiben?
Nicht zwingend bei jedem einzelnen Symbol, TypeScript erlaubt auch export type { A, B } als gebündelte Anweisung für mehrere rein typbasierte Exporte in einer Zeile.
9Erkennt isolatedModules auch Probleme bei default exports?
Ja, ein default export eines reinen Typs erzeugt denselben Fehler wie ein benannter Type-Re-Export und erfordert ebenfalls eine explizite type-Kennzeichnung über export type.
10Lohnt sich isolatedModules auch in kleinen Projekten ohne Pläne für einen Transpiler-Wechsel?
Es schadet nicht und macht den Code portabler, falls sich die Toolchain später ändert. Der Aufwand für die Aktivierung ist bei kleinen, sauber strukturierten Projekten meist gering.