Kuatsu Logo
← Zurück zum Blog
1. Januar 2022 5 Minuten Lesezeit

Ein Leitfaden für die TypeScript Migration

Foto eines MacBooks mit geöffnetem Code-Editor
Bereits seit einiger Zeit stellt TypeScript unsere primäre Sprache dar, sowohl für interne Projekte als auch Kundenprojekte. Bereits seit Beginn arbeiten wir hauptsächlich mit JavaScript (in Kombination mit einigen Frameworks wie React und NodeJS), da die Flexibilität und der vielseitige Anwendungsbereich der Sprache es uns ermöglichen, eine Codebase in derselben Sprache für ein gesamtes Projekt, inklusive Web und Mobile App sowie Backend, zu nutzen. Nicht lang ist es her, dass wir all unsere aktuellen Projekte einer TypeScript Migration durchzogen haben.
Auch wenn jedes JavaScript-Projekt technisch gesehen ein gültiges TypeScript-Projekt ist, da TypeScript ein Superset von JavaScript darstellt, so ist nicht automatisch jedes JavaScript-Projekt auch ein gutes TypeScript-Projekt. Fügen Sie TypeScript zu einer bestehenden JavaScript-Codebase hinzu, so werden alle Variablen, Konstanten und Objekte, welche sich nicht aus dem engeren Kontext für den Compiler ergeben, als vom Typen any betrachtet. Dass dies nicht im Sinne des Erfinders ist, dürfte klar sein. TypeScript wurde eben mit dem Hintergedanken entwickelt, strikte Typen in JavaScript nutzen und auf dynamische Typisierung weitgehend verzichten zu können. Als Entwickler sind Sie also gezwungen, für viele dieser Objekte händisch die Typisierung vorzunehmen. Logisch, dass sich dabei zwei Mal überlegt wird, ob dies wirklich notwendig ist und der Nutzen den Kosten bzw. dem Zeitaufwand überwiegt.
Zwar gibt es inzwischen zahlreiche Tools und Programme, wie „ts-migrate“ von Airbnb (welche TypeScript ebenfalls als offizielle Frontend-Sprache nutzen!), welche die Migration zu TypeScript vereinfachen sollen. Doch auch diese sind selbstredend bei weitem nicht perfekt und verhindern auch nicht das manuelle Typisieren von Objekten. Computer sind schließlich nicht wirklich gut darin, die Semantik hinter Ihrem Code zu verstehen (auch wenn sich dies in Zeiten von Deep Learning und KIs wie GitHub Copilot rasant ändert). Nun stehen wir also vor einem Dilemma: Migrieren – oder die alten Codebases weiter verwalten?

Bevor wir uns also der Frage widmen, ob die Migration bestehender JavaScript-Projekte sinnvoll ist, schauen wir uns zunächst einmal die zwei größten Vorteile von TypeScript an.
  • Der TypeScript Compiler (TSC). Der unserer Ansicht nach größte Vorteil bei der Nutzung von TypeScript liegt im Compiler. JavaScript ist normalerweise eine interpretierte Skriptsprache, welche vom Browser während der Laufzeit interpretiert und ausgeführt wird. Eine Kompilierung ist daher nicht notwendig. TypeScript wird zu JavaScript transkompiliert (wodurch der Browser den kompilierten Code wiederum als normales JavaScript interpretieren kann). Der Vorteil liegt hierbei vor allem darin, dass TypeScript-Code auch zu älteren JavaScript- / ECMAScript-Versionen transkompiliert werden kann. So können Sie auch dann moderne Funktionen wie Promises nutzen, wenn Sie Ihre Anwendungen für ältere Browser bzw. Umgebungen bauen. TypeScript kümmert sich darum, die Features in äquivalente Strukturen der avisierten JavaScript-Version zu kompilieren.
  • Fehlererkennung zur Build-Zeit. Selbstverständlich bringen interpretierte, und nicht kompilierte Sprachen auch Vorteile wie schnellere Entwicklungsabläufe mit sich, jedoch auch einen gravierenden Nachteil. Viele Fehler, die normalerweise ein Compiler auffangen könnte, fallen erst während der Laufzeit auf. TypeScript kann hier in der größten Kategorie nachhelfen, den Typfehlern. Jeder/Jedem JavaScript-Entwickler:in wird es irgendwann einmal passiert sein, dass sie / er versehentlich auf Attribute von undefined oder null zugreifen wollte. TypeScript kann solche Fehler noch verhindern, bevor es die Anwendung in die Staging- oder gar Produktionsumgebung schafft.

Gründe für die TypeScript Migration gibt es also reichlich. Doch lohnt es sich wirklich, auch bestehende, zum Teil riesige Codebases zu migrieren? Die Antwort, die wir uns auf diese Frage gegeben haben, war ein ganz klares Ja. Nachdem wir TypeScript für einige neue Projekte bereits länger verwendet haben und in den Genuss statischer Typisierung in JavaScript gekommen sind, fiel die Entscheidung mehr als leicht. Die Umsetzung hingegen ist eine andere Hausnummer. Viele unserer laufenden Kundenprojekte in JavaScript waren mehrere zehntausend Zeilen lang – wo also anfangen?

Wenn Sie nicht schon zuvor mit TypeScript-„Alternativen“ wie JSDoc gearbeitet haben, ist die Wahrscheinlichkeit hoch, dass Sie recht wenig Ahnung haben, welche Funktion eigentlich welche Typen verwendet. Man sollte sich daher zunächst eine Übersicht über den aktuellen Stand verfassen. Mit welchen Schnittstellen spricht die Anwendung? Welche Daten werden gelesen und geschrieben? Ist die Herkunft dieser Daten typsicher? Eine großzügige Dokumentation des Codes macht sich spätestens hier bemerkbar.

Generell macht TypeScript unserer Ansicht nach so richtig nur mit Nutzung im „Strict“ Modus Sinn. Ohne Aktivierung dessen spendiert TypeScript zwar weit mehr Bewegungsspielraum, indem beispielsweise implizites Any erlaubt wird. Genau das soll jedoch durch die Migration verhindert werden. Wir möchten dynamische und nicht-statische Typen verbieten, weshalb wir uns durch fehlende Nutzung des Strict Mode ein eigenes Loch buddeln.
Als wir unsere ersten Projekte zu TypeScript migriert haben, haben wir unsere Projekte stets zunächst im „Non-Strict“ Modus von TypeScript migriert. Erst im Anschluss haben wir den Strict Mode aktiviert und die restlichen Fehler ausgeweidet. Wir haben jedoch schnell festgestellt, dass dies nicht die effektivste Methode ist. Aktivieren Sie den Strict Mode bereits zu Anfang der Migration. Sie werden sich im weiteren Verlauf viel Kopfzerbrechen sparen. Fehlerbehebungen und Optimierungen, die Sie im ersten Schritt durchführen, müssen teils nach Aktivierung des Strict Modes wieder gänzlich verworfen werden. Auch wenn der Fehler-Tab in Ihrer Entwicklungsumgebung Sie vielleicht einschüchtern mag: Nutzen Sie von Anfang an den Strict Mode.

Ein Großteil der von TypeScript angezeigten Fehler stammt wahrscheinlich gar nicht von Ihrem Code, sondern entspringt dem node_modules Ordner. Dies liegt schlicht daran, dass in den meisten Modulen keine Typdeklarationen von Haus aus dabei sind. Glücklicherweise gibt es von der Open-Source-Community nahezu zu jedem Modul passende Typdeklarationen. Zu einem Modul „some_module“ können Sie diese in der Regel mit $ npm install @types/some_module installieren. Hat Ihre Anwendung jedoch einen sehr spezifischen Anwendungszweck und nutzen Sie daher auch sehr spezielle Libraries, so kann es vorkommen, dass keine passenden Typdeklarationen vorliegen. Anstatt diese Libraries nun jedoch einfach als Any zu deklarieren, investieren Sie die Zeit und legen Sie Typdeklarationen an. Tech Debt ist ein echtes Problem – und Sie möchten sich zu diesem Zeitpunkt nicht darin verwickeln.

Bevor Sie sich den Kleinteilen zuwenden, ist es eine gute Idee, zunächst die verwendeten Klassen, Funktionen und größeren Objekte Ihrer Anwendung zu typisieren. Es ist wahrscheinlich, dass diese immer wieder im Code verwendet werden. Dadurch eignen sich diese als erster Kandidat, da die Typisierung der anderen Variablen und Objekte sich so später weitaus einfacher gestalten wird. Wir haben uns bei unserer Migration daher zunächst Dinge wie Middlewares, Models usw. vorgenommen. Indem wir diesen möglichst genaue Typen gaben, fiel es in den kleineren Funktionen und Abschnitten des Codes später weitaus einfacher, diese zuzuordnen und zu typisieren.
Achten Sie darauf, bei der Typisierung möglichst streng, aber nicht zu streng vorzugehen: TypeScript ist ein sehr mächtiges Werkzeug, und Sie können Ihre Typen so streng machen, wie es Ihnen beliebt. Allerdings, „don’t over-engineer“! Sich in den Typen zu sehr auszutoben macht vom Kosten-Zeit-Faktor ab einem bestimmten Punkt nur noch wenig Sinn.

Ein Großteil der Arbeit wurde nun in recht kurzer Zeit erledigt. Nun geht es jedoch an die Kleinigkeiten. Erfahrungsgemäß reicht es hier meist, Variablen und Co. einen der vorgefertigten Typen wie „string“ zu geben. Auch hier gilt das bekannte Don’t Over-Engineer Prinzip.
Auch Ihre Tests sollten Sie an TypeScript anpassen. Viele der bekannten Test Frameworks sind für TypeScript optimiert und arbeiten damit problemlos.

TypeScript ist womöglich das Beste, was JavaScript in den letzten Jahren passiert ist. Der Trend geht laut den letzten StackOverflow Surveys immer weiter in Richtung statisch typisierte Sprachen. Die Flexibilität von JavaScript und eine sichere Typisierung machen so jedes Projekt zukunftssicher und einfach zu verwalten. Seit unserer vollständigen TypeScript Migration ist es bei uns de-facto Standard, und wir möchten es nicht mehr missen.