Back

TypeScript in Node: Das praktische Setup

TypeScript in Node: Das praktische Setup

Sie schreiben bereits TypeScript für den Browser. Jetzt benötigen Sie es serverseitig – für eine API, ein Build-Script oder SSR. Das Problem: Die meisten Setup-Anleitungen sind veraltet und empfehlen CommonJS-Konfigurationen oder Tools, die nicht mit modernem Node.js übereinstimmen.

Dieser Leitfaden behandelt zwei Ansätze für Ihr TypeScript-Node.js-Setup: Kompilierung mit tsc und Ausführung von JavaScript oder direkte Ausführung von .ts-Dateien mit Nodes nativer Type-Stripping-Funktion. Beide funktionieren. Jeder passt zu unterschiedlichen Szenarien.

Wichtigste Erkenntnisse

  • Setzen Sie "type": "module" in package.json, um ESM standardmäßig für moderne TypeScript-Node.js-Projekte zu aktivieren
  • Verwenden Sie tsc-Kompilierung für Production-Deployments, veröffentlichte Packages und Code, der Enums, Namespaces oder Parameter Properties verwendet
  • Verwenden Sie Nodes natives Type-Stripping für lokale Scripts, Development-Server und schnelle Prototypen
  • Verwenden Sie immer import type für reine Type-Imports, um Laufzeitfehler beim Type-Stripping zu vermeiden
  • Führen Sie tsc --noEmit in der CI aus, da Nodes Type-Stripping keine Typprüfung durchführt

Die Grundlage: Node 24 LTS und ESM

Beginnen Sie mit dieser Basis:

{
  "type": "module"
}

Dies aktiviert ESM standardmäßig. Ihre Imports verwenden ESM-Syntax, und Node löst Module entsprechend auf.

Node 24 ist die aktuelle LTS-Baseline für dieses Setup (kann hier heruntergeladen werden: https://nodejs.org/en/download).

Ansatz 1: Mit tsc kompilieren, JavaScript ausführen

Dieser Ansatz trennt Kompilierung von Ausführung. Verwenden Sie ihn für Production-Deployments, veröffentlichte Packages oder wenn Sie vollständige TypeScript-Feature-Unterstützung benötigen.

tsconfig für Node 24

{
  "compilerOptions": {
    "target": "ES2024",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "rootDir": "src",
    "outDir": "dist",
    "strict": true,
    "skipLibCheck": true,
    "declaration": true,
    "sourceMap": true,
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "lib": ["ES2024"]
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Wichtige Einstellungen in dieser Konfiguration:

  • module: NodeNext und moduleResolution: NodeNext: Entspricht dem tatsächlichen Modul-Auflösungsverhalten von Node
  • verbatimModuleSyntax: Erfordert explizites import type für reine Type-Imports – entscheidend zur Vermeidung von Laufzeitfehlern (siehe TypeScript-Dokumentation: https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax)
  • isolatedModules: Gewährleistet Kompatibilität mit Single-File-Transpilation-Tools

Scripts

{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc --watch"
  }
}

Führen Sie npm run build aus, dann npm start. Das kompilierte JavaScript befindet sich in dist/.

Ansatz 2: Nodes natives TypeScript (Type-Stripping)

Node hat native TypeScript-Unterstützung in Node 22 eingeführt und in Node 24 LTS über Type-Stripping stabilisiert. Verwenden Sie es für Scripts, lokales Tooling oder Development, wenn Sie keine Build-Schritte wünschen.

Offizielle Dokumentation: https://nodejs.org/api/typescript.html

Wie es funktioniert

In Node 24+ führen Sie direkt aus:

node src/index.ts

(Ältere Node-Versionen erforderten experimentelle Flags; Node 24 nicht.)

Kritische Einschränkungen

Nodes natives TypeScript entfernt nur Typen – es führt keine Typprüfung durch. Sie benötigen weiterhin tsc --noEmit in der CI oder Ihrem Editor, um Fehler zu erkennen.

Weitere Einschränkungen:

  • Ignoriert tsconfig.json: Node liest Ihre Compiler-Optionen nicht
  • Erfordert explizite Dateierweiterungen: Schreiben Sie import { foo } from './utils.js', auch wenn die Quelldatei utils.ts ist
  • Respektiert ESM- vs. CJS-Regeln: Ihr package.json-type-Feld ist wichtig
  • Führt kein TypeScript aus node_modules aus: Dependencies müssen kompiliertes JavaScript sein
  • Unterstützt nur löschbare Syntax: Enums, Namespaces und Parameter Properties schlagen fehl, es sei denn, Sie aktivieren --experimental-transform-types

Dateierweiterungen sind wichtig

Für gemischte Modulformate:

  • .mts-Dateien → immer ESM
  • .cts-Dateien → immer CommonJS
  • .ts-Dateien → folgen dem package.json-type-Feld

Laufzeitfehler vermeiden

Verwenden Sie import type für reine Type-Imports:

// Korrekt
import type { Request, Response } from 'express'
import express from 'express'

// Falsch - schlägt zur Laufzeit mit Type-Stripping fehl
import { Request, Response } from 'express'

Aktivieren Sie verbatimModuleSyntax in Ihrer tsconfig, um diese während der Entwicklung zu erkennen, auch wenn Node die Konfiguration zur Laufzeit ignoriert.

Welchen Ansatz verwenden

Verwenden Sie tsc-Kompilierung für:

  • Production-Deployments
  • Veröffentlichte npm-Packages
  • Code, der Enums, Namespaces oder Parameter Properties verwendet
  • Projekte, die Source Maps in Production benötigen

Verwenden Sie natives Type-Stripping für:

  • Lokale Scripts und Tooling
  • Development-Server (kombiniert mit --watch)
  • Schnelle Prototypen
  • SSR-Development-Builds

Ein praktisches Development-Setup

Kombinieren Sie beide Ansätze:

{
  "scripts": {
    "dev": "node --watch src/index.ts",
    "typecheck": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

Development verwendet native Ausführung für Geschwindigkeit. CI führt typecheck aus. Production deployt kompiliertes JavaScript.

Fazit

Das moderne TypeScript-Node.js-Setup ist einfacher, als ältere Anleitungen suggerieren. Verwenden Sie ESM, konfigurieren Sie NodeNext-Modulauflösung und wählen Sie Ihre Ausführungsstrategie basierend auf dem Kontext. Natives Type-Stripping funktioniert für Development und Scripts. Kompilierte Ausgabe funktioniert für Production und Packages. Beide Ansätze teilen denselben Quellcode und dieselbe tsconfig – Sie sind nicht an einen der beiden gebunden.

FAQs

Die Modulauflösung von Node erfordert explizite Dateierweiterungen für ESM. Wenn Sie import from ./utils.js schreiben, sucht Node zur Laufzeit nach genau diesem Pfad, auch wenn Ihre Quelldatei utils.ts ist. Da Type-Stripping Typen entfernt, aber Dateien nicht umbenennt, und tsc .js-Dateien ausgibt, stellt die Verwendung von .js-Erweiterungen in Ihrem Quellcode sicher, dass Imports in beiden Szenarien funktionieren.

Nicht standardmäßig. Enums erfordern Code-Transformation, nicht nur Typ-Entfernung. Sie können das Flag --experimental-transform-types aktivieren, um Enums, Namespaces und Parameter Properties zu unterstützen, aber dies erhöht die Komplexität. Für einfachere Setups sollten Sie const-Objekte mit as const-Assertions als Alternative zu Enums in Betracht ziehen.

Für die meisten Anwendungsfälle nein. Nodes natives Type-Stripping in Node 24 übernimmt die direkte .ts-Ausführung. Tools wie ts-node und tsx sind optionale Komfortfunktionen, die tsconfig.json-Unterstützung, Path-Alias-Auflösung und vollständige TypeScript-Transformationen ohne Flags hinzufügen. Verwenden Sie sie nur, wenn Ihr Setup diese Features benötigt.

Bei Verwendung von nativem Type-Stripping führt Node Ihre .ts-Dateien direkt aus, sodass Zeilennummern in Stack Traces mit Ihrer Quelle übereinstimmen. Für kompilierten Code aktivieren Sie sourceMap in tsconfig.json, und Node verwendet automatisch .js.map-Dateien, um ursprüngliche TypeScript-Positionen in Fehlern und Debugger-Sitzungen anzuzeigen.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay