Einleitung: Warum esModuleInterop mehr als nur ein Häkchen in tsconfig ist
\n\nWenn Sie jemals in TypeScript geschrieben und eine Bibliothek wie express oder lodash importiert haben, sind Sie sicherlich auf den Moment gestoßen, in dem der Compiler import * as express from 'express' beanstandet. Oder Sie verwenden im Gegenteil problemlos import express from 'express', sehen aber im generierten JavaScript-Code eine seltsame Konstruktion wie module.exports.default. Was ist das für eine Magie? Die Magie (oder deren Fehlen) ist das Flag esModuleInterop in Ihrer tsconfig.json.
Dieser Parameter ist eine der häufigsten Fragen auf Stack Overflow (tausende Aufrufe!), und das ist nicht verwunderlich: Ohne sein Verständnis tappt man leicht in die Falle der Modulinkompatibilität. In diesem Tutorial erklären wir nicht nur die Theorie, sondern gehen den Weg vom Problem zur Lösung mit lebendigen Beispielen. Los geht's!
\n\nDas Problem: Konflikt zweier Modulwelten
\n\nTypeScript ist eine Erweiterung von JavaScript, und JavaScript hat historisch gesehen zwei Hauptmodulsysteme:
\n- \n
- CommonJS (CJS) — der Standard von Node.js, verwendet
require()undmodule.exports. Beispiel:const express = require('express');\n - ES Modules (ESM) — der moderne Standard, verwendet
importundexport. Beispiel:import express from 'express';\n
Das Problem ist, dass diese Systeme den Standard-Export (default export) unterschiedlich behandeln. In ESM kann ein Modul einen export default haben und wird als import name from 'module' importiert. In CommonJS gibt es kein Konzept des "Standard-Exports" — es gibt nur das Objekt module.exports, das alles sein kann.
Wenn TypeScript Ihren Code in CommonJS kompiliert (was oft für Node.js nötig ist), muss es entscheiden: Wie stellt man import x from 'y' als require() dar? Ohne esModuleInterop macht TypeScript dies wörtlich: Es erstellt module.exports.default, was den Import von Bibliotheken, die für CommonJS geschrieben wurden, zerstört.
Was ist esModuleInterop?
\n\nesModuleInterop ist ein Flag in tsconfig.json, das zusätzliche Kompatibilität zwischen CommonJS und ES Modules aktiviert. Wenn es auf true gesetzt ist, generiert TypeScript Hilfscode, der es ermöglicht, import foo from 'bar' auch für CommonJS-Module zu schreiben, die keinen default-Export haben.
Standardmäßig ist dieses Flag in TypeScript 4.x und höher auf true gesetzt (wenn Sie --init verwenden), aber in älteren Projekten wurde es oft deaktiviert. Warum? Weil es das Importverhalten ändert und ohne Verständnis zu Fehlern führt.
Wie es in tsconfig.json aussieht
\n{\n "compilerOptions": {\n "esModuleInterop": true,\n // andere Einstellungen...\n }\n}\n\n\nDas Aktivieren dieses Flags aktiviert auch automatisch allowSyntheticDefaultImports, das den Standard-Import aus Modulen ohne tatsächlichen default-Export auf Typebene erlaubt.
Schritt-für-Schritt-Tutorial: Vom Problem zur Lösung
\n\nLassen Sie uns die Situation anhand eines realen Beispiels durchgehen. Angenommen, wir haben eine einfache Bibliothek in CommonJS:
\n\n// myLib.js (CommonJS)\nmodule.exports = function greet(name) {\n return `Hallo, ${name}!`;\n};\n\n\nWir möchten sie in einem TypeScript-Projekt verwenden. Erstellen wir eine Datei index.ts:
// index.ts\nimport greet from './myLib';\nconsole.log(greet('Welt'));\n\n\nSchauen wir uns nun an, was bei der Kompilierung mit verschiedenen esModuleInterop-Einstellungen passiert.
Schritt 1: Ohne esModuleInterop (false)
\n\nStarten wir die Kompilierung mit "esModuleInterop": false. TypeScript gibt einen Fehler aus:
Modul '"myLib"' hat keinen d