Einleitung: Warum Refactoring von Legacy-Code kein Luxus, sondern eine Notwendigkeit ist
\nIn der modernen Webentwicklung ist TypeScript zum De-facto-Standard für große Projekte geworden. Viele Unternehmen und Startups stehen jedoch vor einem Problem: Die in reinem JavaScript oder frühen, nicht-strengen Versionen von TypeScript geschriebene Codebasis verwandelt sich im Laufe der Zeit in einen „Sumpf“. Fehlende Typen, aufgeblähte Funktionen, implizite Abhängigkeiten und veraltete Muster – all das verlangsamt die Entwicklung und erhöht die Anzahl der Fehler.
\nRefactoring von Legacy-Code ist der Prozess der Verbesserung der internen Struktur eines Programms, ohne sein externes Verhalten zu ändern. Im Kontext von TypeScript gewinnt es einen besonderen Wert: Wir bereinigen nicht nur den Code, sondern erhalten auch ein leistungsstarkes Typsystem, das Fehler bereits zur Kompilierzeit verhindert. In diesem Artikel werden wir einen schrittweisen Refactoring-Plan durchgehen, von der ersten Analyse bis zum abschließenden Testen, mit realen Beispielen in TypeScript.
\n\n1. Analyse und Inventarisierung: Wo anfangen?
\nDer erste und wichtigste Schritt ist zu verstehen, womit wir es zu tun haben. Man kann nicht einfach alles auf einmal umschreiben. Das führt zu Chaos und Zeitverlust. Der richtige Ansatz ist ein Audit der Codebasis.
\n- \n
- Identifizieren Sie „Hot Spots“. Finden Sie Module, die am häufigsten geändert werden oder die meisten Fehler enthalten. Verwenden Sie Tools wie
git logoder SonarQube. \n - Ermitteln Sie implizite Typen. In Legacy-JS-Code gibt es oft Funktionen, die
anyakzeptieren oder@ts-ignoreverwenden. Erstellen Sie eine Liste solcher Stellen. \n - Überprüfen Sie Abhängigkeiten. Veraltete Bibliotheken (z. B.
lodash3.x) können die Migration zu modernen TypeScript-APIs behindern. \n - Schreiben Sie Tests. Decken Sie vor jedem Refactoring die wichtigsten Codebereiche mit Tests ab. Das ist Ihr „Sicherheitsnetz“. \n
Beispiel: Sie haben eine JS-Funktion, die Benutzerdaten verarbeitet. Anstatt sie sofort umzuschreiben, fügen Sie Unit-Tests mit Jest oder Vitest hinzu:
\n// legacy.js\nfunction processUser(data) {\n return { name: data.name, age: data.age + 1 };\n}\n\n// test.js\nimport { processUser } from './legacy';\ntest('processUser increments age', () => {\n expect(processUser({ name: 'Alice', age: 30 })).toEqual({ name: 'Alice', age: 31 });\n});\n\nErst danach können Sie mit dem Refactoring beginnen.
\n\n2. Schrittweise Migration zu strenger Typisierung
\nTypeScript bietet mehrere Strenge-Stufen. Für Legacy-Code wird empfohlen, mit "strict": false zu beginnen und die Messlatte schrittweise zu erhöhen. Das plötzliche Aktivieren aller Optionen kann Hunderte von Dateien beschädigen.
Wichtige Schritte:
\n- \n
- Aktivieren Sie
noImplicitAny. Dies zwingt Sie, Typen für alle Parameter und Rückgabewerte explizit anzugeben. Das kann zunächst schmerzhaft sein, aber so finden Sie die gefährlichsten Stellen. \n - Verwenden Sie
strictNullChecks. Eine der Hauptursachen für Fehler sindnullundundefined. Dieses Flag zwingt Sie, diese explizit zu behandeln. \n - Fügen Sie
noUnusedLocalsundnoUnusedParametershinzu. Sie helfen, toten Code zu entfernen. \n
Beispiel für das Refactoring einer Funktion mit impliziten Typen:
\n// Vor dem Refactoring (Legacy JS)\nfunction calculateTotal(price, tax) {\n return price + (price * tax);\n}\n\n// Nach dem Refactoring (TypeScript)\ninterface PriceData {\n price: number;\n tax: number;\n}\n\nfunction calculateTotal({ price, tax }: PriceData): number {\n if (price < 0 || tax < 0) {\n throw new Error('Price and tax must be non-negative');\n }\n return price + (price * tax);\n}\n\nBeachten Sie: Wir haben nicht nur Typen hinzugefügt, sondern die API durch die Verwendung eines Interfaces und einer Validierung auch sicherer gemacht.
\n\n