Node.js + TypeScript = Nie wieder JavaScript

Wer mit Node.js entwickelt, schreibt JavaScript – oder muss umständlich TypeScript konfigurieren. Doch beides hat nun bald ein Ende.

In Pocket speichern vorlesen Druckansicht 25 Kommentare lesen
JavaScript-Logo vor Code

(Bild: Trismegist san/Shutterstock.com)

Lesezeit: 9 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

"Nie wieder JavaScript!"

the next big thing – Golo Roden

Golo Roden ist Gründer und CTO von the native web GmbH. Er beschäftigt sich mit der Konzeption und Entwicklung von Web- und Cloud-Anwendungen sowie -APIs, mit einem Schwerpunkt auf Event-getriebenen und Service-basierten verteilten Architekturen. Sein Leitsatz lautet, dass Softwareentwicklung kein Selbstzweck ist, sondern immer einer zugrundeliegenden Fachlichkeit folgen muss.

Diesen Satz habe ich in den vergangenen fünfzehn Jahren, in denen ich mit Node.js gearbeitet habe, immer wieder gehört. Die Liste der Vorwürfe war lang: JavaScript sei keine richtige Programmiersprache. JavaScript sei nicht für komplexe Projekte im Enterprise-Bereich geeignet. JavaScript sei dies nicht, JavaScript sei jenes nicht. Natürlich trifft man in fünfzehn Jahren auch auf einige, die JavaScript mögen, aber die Mehrheit der Entwicklerinnen und Entwickler lehnt JavaScript rundheraus ab. Viele schreiben es nur aus Notwendigkeit, nicht aber aus Überzeugung. JavaScript sei seltsam, das ist eine immer noch weitverbreitete Meinung. Wenn auch Sie dieser Meinung sind, dann habe ich heute eine richtig gute Neuigkeit für Sie: Es ist vorbei, Sie werden nie wieder JavaScript schreiben müssen!

Vielleicht sagen Sie jetzt:

"Naja, eigentlich stimmt das so nicht ganz, denn auch heute schon muss ich JavaScript nicht zwingend schreiben. Schließlich gibt es TypeScript."

Und das ist richtig. Seit Microsoft vor zwölf Jahren TypeScript auf den Markt gebracht hat, ist es praktisch zur De-facto-Sprache in der modernen Webentwicklung geworden. Und das ist kein Zufall, denn TypeScript macht vieles richtig: Angefangen beim sehr guten und durchdachten optionalen statischen Typsystem bis hin zum ebenfalls sehr zuverlässigen Compiler. TypeScript können viele Entwicklerinnen und Entwickler nicht mehr aus ihrem Arbeitsalltag wegdenken. Wäre da nicht das Tooling, denn wo Licht ist, ist bekanntermaßen auch Schatten.

Empfohlener redaktioneller Inhalt

Mit Ihrer Zustimmmung wird hier ein externes YouTube-Video (Google Ireland Limited) geladen.

Ich bin damit einverstanden, dass mir externe Inhalte angezeigt werden. Damit können personenbezogene Daten an Drittplattformen (Google Ireland Limited) übermittelt werden. Mehr dazu in unserer Datenschutzerklärung.

Ich weiß aus eigener Erfahrung, dass es Lustigeres gibt, als ein Node.js-Projekt mit TypeScript aufzusetzen. Es ist nämlich nicht damit getan, TypeScript zu installieren und zu konfigurieren, sondern es soll schließlich auch das ganze Drumherum mit TypeScript funktionieren: von der Codeformatierung über das Linting bis hin zu den Tests. Bis alle Bausteine so konfiguriert sind, dass alles zueinander passt und miteinander harmoniert, vergeht viel Zeit.

Da werde ich sicherlich nicht der Einzige sein, der sich in den vergangenen zwölf Jahren immer wieder gefragt hat, warum eigentlich Node.js nicht TypeScript nativ unterstützt. Warum muss man als Entwicklerin oder als Entwickler im Node-Universum immer diesen extrem lästigen Eiertanz aufführen, bis TypeScript endlich so läuft, wie es das soll? Wäre es nicht viel einfacher, wenn Node.js TypeScript von Haus aus unterstützen würde? Dann könnte man sich den ganzen Compiler sparen, die Tests würden automatisch mit TypeScript funktionieren, und das ganze sonstige Tooling wäre vermutlich schnell umgestellt. Denn wenn TypeScript erst einmal im Fundament richtig funktionieren würde, würde der Rest wohl rasch folgen. Also: Wäre es nicht fantastisch, wenn Node.js TypeScript nativ unterstützen würde? Wäre das nicht ein riesiger Schritt nach vorn?

Und damit kommen wir zu der versprochenen großen Neuigkeit: Seit Anfang Juli 2024 enthält Node.js native Unterstützung für TypeScript!

Weil viele jetzt wahrscheinlich neugierig sind, wie das funktioniert und vor allem auch, wie gut das funktioniert, habe ich ein paar Antworten. Zuerst einmal: Die Unterstützung für TypeScript ist aktuell noch experimentell, sie ist nur in den Nightly Builds vorhanden, und man muss beim Starten von Node.js ein passendes Flag angeben, nämlich --experimental-strip-types. Das Ganze wird sicherlich bald in einem regulären Release enthalten sein und vielleicht sogar in Node.js 23, das uns voraussichtlich im Oktober erwartet.

Die Implementierung ist zunächst ganz simpel: Die Typ-Annotationen im Code werden zur Laufzeit schlicht und ergreifend verworfen. Das heißt, es findet keinerlei Typenprüfung statt. Das war eine bewusste Entscheidung des Node-Teams. Wenn Sie die Typenprüfung haben wollen, brauchen Sie weiterhin den TypeScript-Compiler. Das ist bei Node.js in Zukunft also genauso wie bei Deno und Bun. Tatsächlich ist das kein Zufall, denn unter der Haube verwendet Node.js für das Type-Stripping denselben Mechanismus wie Deno.

Dieses Type-Stripping ist dabei übrigens wörtlich zu nehmen: Es wird nämlich nicht der Code transformiert, sondern nur die Typ-Annotationen entfernt. Alles, was theoretisch von TypeScript nach JavaScript transformiert werden müsste, um zu funktionieren, wird daher aktuell nicht funktionieren. Dazu zählen etwa Features wie Enums oder Namespaces. Das heißt, zum jetzigen Zeitpunkt ist noch keine volle Unterstützung für TypeScript gegeben. Aber man darf nicht vergessen, dass wir derzeit noch über einen sehr frühen Entwicklungsstand sprechen, und dass sich im Laufe der Zeit noch einiges ändern kann. Dazu komme ich aber gleich noch.

Technisch basiert das Ganze auf dem npm-Modul @swc/wasm-typescript. SWC ist dabei der Speedy Web Compiler, in Rust geschrieben und dementsprechend schnell. Das besagte npm-Modul enthält den TypeScript-Parser von SWC, aber nicht (wie man das vielleicht erwarten würde) als Rust-Code, sondern als WebAssembly-Code. Auf diesem Weg kann Node.js das Ganze einfach ausführen, ohne dass Rust auf dem jeweiligen System installiert sein müsste. Das finde ich persönlich sehr schlau gelöst. Und genau derselbe Mechanismus steckt übrigens auch in Deno.

Für die erste Version muss man mit einigen Einschränkungen leben wie der fehlenden Unterstützung für einige Sprachmerkmale. Dateien müssen außerdem zwingend die Dateiendung .ts tragen, sie dürfen nicht als .js benannt sein. Darüber hinaus wird TypeScript nur im Code Ihres eigenen Projekts unterstützt, nicht aber im node_modules-Verzeichnis. Man wollte verhindern, dass Entwicklerinnen und Entwickler npm-Module nur noch als reine TypeScript-Anwendung veröffentlichen, was die Abwärtskompatibilität zunichtemachen würde. Also muss beim Veröffentlichen von Modulen weiterhin kompiliert werden. Spannend finde ich übrigens, dass auch nach dem Type-Stripping Fehler zur Laufzeit mit den richtigen Zeilen- und Spaltennummern im Quellcode angegeben werden können. Das könnte darauf hindeuten, dass sogar Sourcemaps unterstützt werden – tatsächlich ist die Lösung aber weitaus einfacher: Beim Type-Stripping werden die Typannotationen nämlich einfach mit Leerzeichen überschrieben, sodass alle Positionen im Quellcode erhalten bleiben.

Auf absehbare Zeit soll das Type-Stripping dann von Node.js entkoppelt werden, sodass man nicht auf eine spezifische Node-Version mit einer spezifischen TypeScript-Version angewiesen ist. Man will zu einem Modell kommen wie mit npm, das bei Node.js mitgeliefert wird, sich aber unabhängig aktualisieren lässt. Eine Herausforderung ist, dass man TypeScript pro Projekt installieren möchte und nicht global systemweit. Wie das gelöst werden soll, ist noch offen. Es gibt zwar bereits ein npm-Modul namens amaro, das als Wrapper um @swc/wasm-typescript fungiert. In der Dokumentation dieses Moduls heißt es aktuell jedoch noch, dass man es global installieren solle. Das steckt also alles noch in den Kinderschuhen und wird noch seine Zeit brauchen.

Wenn dann irgendwann dafür eine Lösung gefunden ist, folgen die Schritte drei und vier der Roadmap: Zunächst wird dabei die Performance verbessert und die Kommunikation zwischen Node und dem TypeScript-Compiler effizienter gestaltet. In Schritt vier geht es dann schließlich um neue Features. Frühestens dann ist damit zu rechnen, dass weitere Sprachmerkmale von TypeScript unterstützt werden, die über das reine Type-Stripping hinausgehen. Ein Feature, das dann ebenfalls dazugehören soll, ist die Unterstützung der tsconfig.json. Ich finde es überraschend, dass ausgerechnet das noch so lange auf sich warten lassen wird. Aber gut: Es ist, wie es ist.

Insgesamt muss ich zugeben, dass ich positiv überrascht bin, dass dieses Feature nun grundsätzlich Einzug in Node.js findet. Deno und Bun hatten in den letzten Jahren einen deutlichen Vorsprung, der jetzt zunehmend kleiner wird. Ich habe an den letzten Versionen von Node.js stets viel zu kritisieren gehabt, aber das ist ein großer Schritt in die richtige Richtung. Auch wenn ich vielleicht nicht mit allen Details glücklich bin, ist das grundsätzlich dennoch ein hervorragender Schritt. Ich hoffe nur, dass das Thema nicht genauso versumpft wie andere, etwa die Single Executable Applications, die mit Node 19.7 eingeführt wurden. Oder die Verwendung der V8-eigenen Sandbox, um die Sicherheit bei heruntergeladenen Modulen zu verbessern, was in Anlehnung an das Sandbox-basierte Sicherheitsmodell von Deno erfolgte. Seit Node 20 ist daran jedoch ebenfalls nichts mehr passiert.

Deshalb bin ich zwar positiv überrascht, aber meine Begeisterung hält sich derzeit noch in Grenzen, weil ich skeptisch bin, wie viel von der angekündigten Roadmap tatsächlich umgesetzt werden wird und vor allem, wann. Denn es ist schön, wenn Deno und Bun dafür sorgen, dass wieder mehr frischer Wind in Node.js hineinkommt. Allerdings ist das nur solange schön, wie die Features auch tatsächlich fertig umgesetzt werden und nicht als angefangene Baustellen liegenbleiben. Da bin ich noch etwas verhalten. Aber: Die Zukunft wird zeigen, was das Node-Team zum Beispiel im Oktober in Version 23 veröffentlichen wird. Bis dahin müssen wir uns gedulden und uns überraschen lassen. (rme)