require vs import in TypeScript: Schritt-für-Schritt-Tutorial für Verwirrte

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

require(x) vs import x: Warum das der ewige Kopfschmerz eines TypeScript-Entwicklers ist

\n\n

Wenn Sie jemals in TypeScript geschrieben und einen Fehler wie Cannot use import statement outside a module oder require is not defined gesehen haben – Sie sind nicht allein. Auf Stack Overflow hat die Frage "The difference between 'require(x)' and 'import x'" Zehntausende von Aufrufen gesammelt. Und das ist kein Zufall: Die Verwirrung zwischen den beiden Modulsystemen ist eine der häufigsten Fallen, in die selbst erfahrene Entwickler tappen.

\n\n

In diesem Tutorial werden wir nicht nur die Unterschiede auflisten. Wir werden den Weg von völligem Unverständnis bis zur sicheren Wahl der richtigen Syntax gehen. Sie werden erfahren, wie TypeScript Ihren Code kompiliert, warum require immer noch lebt und wann Sie import verwenden sollten.

\n\n

1. Historischer Kontext: Woher die beiden Systeme kommen

\n\n

Um den Unterschied zu verstehen, müssen wir in die Geschichte von JavaScript eintauchen. Ursprünglich hatte Node.js kein eingebautes Modulsystem. Entwickler erfanden CommonJS – eine synchrone Methode, um Module über require() einzubinden. Dieser Ansatz funktioniert serverseitig, war aber für Browser ungeeignet – dort war asynchrones Laden erforderlich.

\n\n

Später kam der Standard ES Modules (ESM) – die offizielle Modulsyntax von ECMAScript. TypeScript, als Erweiterung von JS, unterstützt beide Varianten. Aber genau hier beginnt die Verwirrung: Sie schreiben import, und der Compiler wandelt es in require um – oder umgekehrt.

\n\n

2. Syntax: Die Hauptunterschiede

\n\n

Lassen Sie uns aufschlüsseln, wie beide Ansätze aussehen.

\n\n

CommonJS (require)

\n\n
// Import eines gesamten Moduls\nconst fs = require('fs');\n\n// Import eines Teils eines Moduls (Destrukturierung)\nconst { readFile, writeFile } = require('fs');\n\n// Export\nmodule.exports = { myFunction, myVariable };\nexports.myFunction = () => { ... };
\n\n

ES Modules (import)

\n\n
// Import eines gesamten Moduls\nimport fs from 'fs';\n\n// Import benannter Exporte\nimport { readFile, writeFile } from 'fs';\n\n// Import von allem als Objekt\nimport * as fs from 'fs';\n\n// Export\nexport const myVariable = 42;\nexport function myFunction() { ... }\nexport default class MyClass { }
\n\n

Auf den ersten Blick besteht der Unterschied nur in der Syntax. Aber schauen wir tiefer.

\n\n

3. Statischer vs dynamischer Import

\n\n

Dies ist der entscheidende Unterschied, der beeinflusst, wie Ihr Code funktioniert.

\n\n
    \n
  • require – dynamisch. Sie können es überall aufrufen, innerhalb einer Bedingung oder Funktion. Das Modul wird zum Zeitpunkt des Aufrufs geladen.
  • \n
  • import – statisch. Importe werden zur Kompilierzeit aufgelöst, bevor der Code ausgeführt wird. Sie müssen immer auf der obersten Ebene der Datei stehen.
  • \n
\n\n

Beispiel für dynamisches require:

\n\n
let myModule;\nif (process.env.NODE_ENV === 'production') {\n  myModule = require('./production-config');\n} else {\n  myModule = require('./dev-config');\n}
\n\n

So ein Trick funktioniert mit import nicht – es gäbe einen Syntaxfehler. Aber dafür gibt es den dynamischen import() (gibt ein Promise zurück):

\n\n
if (condition) {\n  const module = await import('./some-module');\n  module.doSomething();\n}
\n\n

4. Wie TypeScript Module kompiliert

\n\n

Alles hängt von der Einstellung module in der tsconfig.json ab. Hier sind die wichtigsten Optionen:

\n\n
    \n
  • "module": "commonjs" – Ihr import wird zu require. Der Code funktioniert in Node.js ohne zusätzliche Einstellungen.
  • \n
  • "module": "esnext" – import bleibt wie es ist. Geeignet für moderne Browser oder Bundler (Webpack, Vite).
  • \n
  • "module": "node16" oder "nodenext" – TypeScript entscheidet selbst

Blogs

Buchempfehlungen