verbatimModuleSyntax: Import/Export-Verhalten explizit kontrollieren
AI generated
type
TypeScript
verbatimModuleSyntax
Import- und Export-Verhalten ohne Compiler-Magie

Kein stilles Wegoptimieren von Importen mehr: mit verbatimModuleSyntax steht im Output exakt das, was im Quellcode geschrieben wurde.

8 Min. Lesezeit TypeScript 5.x Module System

1. Die Geschichte: von isolatedModules zu verbatimModuleSyntax

Vor TypeScript 5.0 gab es mehrere separate, teils widersprüchliche Flags, die Modulverhalten steuerten: isolatedModules, importsNotUsedAsValues und preserveValueImports. Jedes davon löste einen Teilaspekt desselben Grundproblems, aber die Kombination war schwer vorhersehbar.

verbatimModuleSyntax fasst diese drei Flags zu einer einzigen, klaren Regel zusammen: alles, was im Quellcode als Import oder Export geschrieben steht, bleibt im kompilierten Output erhalten, außer es ist explizit mit type markiert.

Seit der Einführung raten die TypeScript-Release-Notes ausdrücklich dazu, die drei alten Flags durch das neue zu ersetzen, weil es dieselben Ziele mit deutlich weniger überraschendem Verhalten erreicht.

Für Teams, die schon länger mit TypeScript arbeiten, ist dieser Schritt vergleichbar mit der Ablösung mehrerer älterer Linting-Regeln durch eine einzige, klar dokumentierte Regel: weniger Konfigurationsfläche, weniger Kombinationsmöglichkeiten, die zu widersprüchlichem Verhalten führen können.

2. Was sich konkret ändert: Import/Export bleibt erhalten

Ohne verbatimModuleSyntax entfernt tsc standardmäßig Importe, die im Code ausschließlich als Typ verwendet werden, automatisch aus dem JavaScript-Output. Das klingt praktisch, führt aber zu Überraschungen, wenn ein Import auch einen Seiteneffekt haben sollte, etwa das Laden von CSS oder das Registrieren eines Polyfills.

Mit aktiviertem Flag entscheidet ausschließlich die geschriebene Syntax, nicht die Typinferenz des Compilers, ob ein Import erhalten bleibt: ein normaler import { Foo } from './foo' bleibt im Output stehen, ein import type { Foo } from './foo' verschwindet garantiert vollständig.

Diese Vorhersagbarkeit ist besonders bei Nebeneffekt-Importen wichtig, weil der Compiler früher anhand von Heuristiken raten musste, ob ein Import nur wegen seiner Typen oder auch wegen eines Seiteneffekts vorhanden war, was in Randfällen zu falsch entfernten Importen führte.

Solche falsch entfernten Importe sind besonders tückisch, weil sie sich in der lokalen Entwicklung oft gar nicht bemerkbar machen, sondern erst im Produktions-Build auftreten, wenn ein fehlendes Stylesheet oder ein fehlender Polyfill zu einem schwer nachvollziehbaren Laufzeitfehler führt.


// Ohne verbatimModuleSyntax: tsc entscheidet selbst, was bleibt.
// Mit verbatimModuleSyntax: die Syntax entscheidet.

import type { User } from "./types";       // verschwindet immer im Output
import { formatUser } from "./format";      // bleibt immer im Output
import "./register-polyfill";                // Seiteneffekt bleibt garantiert erhalten

3. import type und export type als Pflicht

Sobald verbatimModuleSyntax aktiv ist, meldet TypeScript einen Fehler, wenn ein ausschließlich als Typ genutztes Symbol ohne type-Modifier importiert oder re-exportiert wird. Das entspricht im Kern den bereits von isolatedModules bekannten Regeln, wird aber konsequenter durchgesetzt.

Praktisch bedeutet das: gemischte Importe, die sowohl Werte als auch Typen aus demselben Modul beziehen, müssen den type-Modifier pro Symbol setzen, etwa import { type User, formatUser } from './module', statt zwei getrennte Import-Anweisungen zu benötigen.

Diese Inline-Syntax für gemischte Importe wurde bereits mit TypeScript 4.5 eingeführt und ist unter verbatimModuleSyntax der empfohlene Standardweg, weil sie explizit bleibt, ohne den Code unnötig zu verlängern.


// Gemischter Import: Wert und Typ aus demselben Modul
import { type User, formatUser } from "./user";

export function greet(user: User): string {
  return formatUser(user);
}

// Re-Export erfordert ebenfalls den type-Modifier:
export type { User };

4. Auswirkungen auf CommonJS-Interop (esModuleInterop)

verbatimModuleSyntax ändert auch, wie TypeScript mit CommonJS-Interop umgeht. Ohne das Flag durfte ein Default-Import von einem CommonJS-Modul ohne echten Default-Export trotzdem funktionieren, weil tsc beim Kompilieren einen synthetischen Default-Export erzeugte.

Mit verbatimModuleSyntax bleibt diese Synthese für die Typprüfung zwar bestehen, sofern esModuleInterop aktiv ist, aber die tatsächliche Modul-Syntax im Output entspricht exakter dem, was in der Zielumgebung, etwa Node.js im ESM-Modus, tatsächlich erwartet wird.

Für Projekte, die zwischen CommonJS und ESM wechseln oder beides gleichzeitig unterstützen müssen (Dual-Package-Hazard), macht diese Präzision den Unterschied zwischen einem Build, der in beiden Umgebungen tatsächlich läuft, und einem, der nur in einer davon funktioniert.

5. Fehlermeldungen verstehen und beheben

Die typische Fehlermeldung lautet sinngemäß, dass ein Symbol nur als Typ verwendet wird und daher mit type importiert werden muss, sofern verbatimModuleSyntax aktiv ist. Das ist inhaltlich fast identisch mit der isolatedModules-Meldung, aber strikter formuliert.

Ein zweiter häufiger Fehlerfall betrifft export = und import =, die alte CommonJS-Interop-Syntax: unter verbatimModuleSyntax ist diese Syntax nur noch erlaubt, wenn das Modulsystem auf CommonJS-kompatible Ausgabeformate eingestellt ist, bei reinen ESM-Zielen wird sie abgelehnt.

Die meisten modernen Editoren mit TypeScript-Sprachserver bieten für beide Fehlerklassen einen automatischen Quick-Fix an, der die Importzeile korrekt umschreibt, sodass die Migration selten manuelles Durchsuchen jeder Datei erfordert.

6. Zusammenspiel mit isolatedModules und importsNotUsedAsValues

TypeScript 5.0 markiert importsNotUsedAsValues und preserveValueImports offiziell als veraltet zugunsten von verbatimModuleSyntax. Werden beide alten Flags gleichzeitig mit dem neuen aktiviert, meldet der Compiler einen Konfigurationsfehler, da sie sich gegenseitig ausschließen.

isolatedModules darf hingegen weiterhin parallel aktiv sein, ist aber redundant, weil verbatimModuleSyntax dessen Regeln als Teilmenge bereits durchsetzt. Viele Projekt-Vorlagen aktivieren trotzdem beide, um die Absicht im Code explizit zu dokumentieren.

Für Bibliotheksautoren ist die Kombination beider Flags besonders empfehlenswert, weil sie garantiert, dass die eigene Codebasis unabhängig vom Build-Tool des Konsumenten (tsc, esbuild, SWC) identisches Verhalten zeigt.

7. Migration von älteren TypeScript-Projekten

Der erste Schritt ist, importsNotUsedAsValues und preserveValueImports aus der tsconfig zu entfernen, weil sie mit verbatimModuleSyntax nicht koexistieren dürfen. Danach wird das neue Flag aktiviert und tsc --noEmit ausgeführt, um alle betroffenen Stellen zu sammeln.

In großen Codebasen lohnt sich ein automatisiertes Vorgehen: viele ESLint-Setups bieten mit der Regel consistent-type-imports aus @typescript-eslint eine Autofix-Option, die vor der eigentlichen Flag-Aktivierung bereits fast alle nötigen type-Modifier ergänzt.

Nach der Migration ist es sinnvoll, die ESLint-Regel dauerhaft aktiv zu lassen, damit neue Verstöße bereits beim Schreiben des Codes im Editor auffallen, statt erst beim nächsten tsc-Lauf oder, schlimmer, erst im Produktions-Build.

8. Editor-Unterstützung: Auto-Import mit korrektem type-Keyword

Moderne TypeScript-Sprachserver erkennen ein aktiviertes verbatimModuleSyntax und passen automatisch generierte Import-Vorschläge entsprechend an: wird ein Symbol per Auto-Import eingefügt, das ausschließlich als Typ genutzt wird, ergänzt der Editor automatisch den type-Modifier.

Dieses Verhalten reduziert die Reibung der Migration erheblich, weil Entwickler in der täglichen Arbeit selten manuell an die richtige Syntax denken müssen, der Editor erledigt die Unterscheidung zwischen Typ und Wert automatisch im Hintergrund.

Bei älteren Editor-Versionen oder Sprachserver-Konfigurationen, die das Flag nicht kennen, kann es dennoch zu falsch generierten Importen kommen, weshalb ein aktueller TypeScript-Sprachserver Teil der Migrations-Checkliste sein sollte.

9. Wann man verbatimModuleSyntax NICHT aktivieren sollte

In sehr alten Codebasen mit umfangreicher Nutzung von namespace-basierten Modulsystemen und globalen Skripten kann die Migration unverhältnismäßig aufwendig sein, verglichen mit dem tatsächlichen Nutzen für ein Projekt, das ohnehin nicht auf einen Single-File-Transpiler wechseln wird.

Auch bei Projekten kurz vor einem geplanten kompletten Rewrite lohnt sich der Migrationsaufwand meist nicht mehr, weil der Nutzen erst langfristig durch weniger Build-Überraschungen entsteht und ein baldiger Rewrite diesen langfristigen Vorteil ohnehin zunichtemacht.

Die folgende Tabelle stellt Szenarien gegenüber, in denen sich die Aktivierung lohnt oder eher zurückgestellt werden sollte.

Szenario Empfehlung Begründung Aufwand
Neues Projekt mit Vite/esbuild Sofort aktivieren Verhindert Klassen von Build-Fehlern von Anfang an Keiner
Bestehendes Projekt, nur tsc als Build Empfohlen, aber nicht dringend tsc kompiliert ohnehin korrekt Gering bis mittel
Bibliothek mit vielen Konsumenten Aktivieren Garantiert konsistentes Verhalten über alle Build-Tools Mittel
Legacy-Codebase mit Namespace-Modulen Zurückstellen Migrationsaufwand übersteigt kurzfristigen Nutzen Hoch

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

verbatimModuleSyntax

Kernprinzip

Syntax entscheidet, nicht Typinferenz, ob ein Import im Output bleibt.

Ersetzt

isolatedModules-Teilaspekte, importsNotUsedAsValues, preserveValueImports.

Pflicht-Syntax

import type / export type für alle reinen Typ-Importe und -Exporte.

Editor-Support

Auto-Import ergänzt den type-Modifier automatisch bei aktuellem Sprachserver.

11. FAQ: verbatimModuleSyntax

1Muss ich verbatimModuleSyntax in jedem neuen Projekt aktivieren?
Verpflichtend ist es nicht, aber für neue Projekte mit modernen Build-Tools wie Vite ist es die empfohlene Voreinstellung, weil es Klassen von Build-Fehlern von Anfang an ausschließt.
2Kann ich importsNotUsedAsValues und verbatimModuleSyntax gleichzeitig aktivieren?
Nein, TypeScript meldet einen Konfigurationsfehler, weil beide Flags dasselbe Problem auf inkompatible Weise lösen. Die alten Flags müssen vor der Aktivierung entfernt werden.
3Bricht verbatimModuleSyntax bestehende export = Anweisungen?
Nur bei Zielen mit reinem ESM-Output. Bei CommonJS-kompatiblen module-Einstellungen bleibt die alte Syntax weiterhin erlaubt.
4Wie unterscheidet sich das von isolatedModules in der Praxis?
isolatedModules verhindert nur Code, der bei Single-File-Transpilation kaputtgehen würde. verbatimModuleSyntax geht weiter und erzwingt zusätzlich, dass Import/Export-Syntax exakt wie geschrieben im Output erscheint.
5Gibt es einen automatischen Codemod für die Migration?
Die ESLint-Regel consistent-type-imports aus dem typescript-eslint-Projekt bietet einen Autofix, der vor der Flag-Aktivierung die meisten benötigten type-Modifier automatisch ergänzt.
6Wirkt sich das Flag auf die Laufzeitgröße des Bundles aus?
Indirekt ja, weil garantiert type-only Importe vollständig entfernt werden und keine unnötigen Modulreferenzen im Bundle verbleiben, was insbesondere bei großen Typdefinitionsmodulen den Output schlanker macht.
7Funktioniert verbatimModuleSyntax auch mit älteren Node.js-CommonJS-Projekten?
Ja, solange module in der tsconfig auf ein CommonJS-kompatibles Format gesetzt ist, bleibt export = und import = weiterhin nutzbar.
8Muss ich bei einem Interface-Import immer type explizit schreiben?
Ja, sobald das Flag aktiv ist, führt ein Import eines Interfaces ohne type-Modifier zu einem Compiler-Fehler, unabhängig davon, ob das Interface an anderer Stelle im selben Modul auch als Wert existiert.
9Kann verbatimModuleSyntax versehentlich Seiteneffekt-Importe entfernen?
Nein, das ist genau das Problem, das es löst: reine Seiteneffekt-Importe ohne benannte Symbole bleiben garantiert erhalten, unabhängig von jeglicher Typinferenz.
10Ist verbatimModuleSyntax Teil des strict-Modus?
Nein, es ist ein eigenständiges Flag und wird von strict: true nicht automatisch aktiviert. Es muss explizit in der tsconfig gesetzt werden.