ProCust
Clean Code in TypeScript: Prinzipien für wartbare Projekte

Clean Code in TypeScript: Prinzipien für wartbare Projekte

Praktische Clean-Code-Regeln für TypeScript: strikte Typen, sprechende Namen, kleine Funktionen und geprüfte Grenzen, mit Beispielen und typischen Fehlern.

Kurz gefasst: Clean Code in TypeScript bedeutet vor allem, das Typsystem für sich arbeiten zu lassen: strict einschalten, any vermeiden, Daten an den Systemgrenzen validieren und unmögliche Zustände gar nicht erst darstellbar machen. Dazu kommen die allgemeinen Regeln, die in jeder Sprache gelten: sprechende Namen, kleine Funktionen mit einer Aufgabe und Code, der nach Fachlichkeit statt nach Technik gegliedert ist. Das Ergebnis ist Software, die sich auch nach Jahren noch sicher ändern lässt.

Warum Clean Code in TypeScript ein eigenes Thema ist

Die klassischen Clean-Code-Prinzipien stammen aus Java- und C#-Projekten. Vieles davon gilt weiter, manches passt aber nicht zu TypeScript. Tiefe Klassenhierarchien und viele Interfaces mit nur einer Implementierung machen TypeScript-Code eher schwerer lesbar. Dafür bietet die Sprache Werkzeuge, die andere nicht haben: strukturelle Typen, Union Types, Type Narrowing und Typinferenz.

Sauberer TypeScript-Code nutzt diese Werkzeuge, um Fehler vom Laufzeitpunkt in den Editor zu verlagern. Jeder Fehler, den der Compiler findet, ist einer, den kein Nutzer sieht.

Das Typsystem als Sicherheitsnetz

Strict Mode von Anfang an

Die Option strict in der tsconfig.json aktiviert unter anderem strictNullChecks und noImplicitAny. Ohne sie ist TypeScript deutlich nachsichtiger, und viele Fehler rutschen durch. In neuen Projekten gehört strict von Tag eins dazu. Sinnvoll ergänzt wird es durch noUncheckedIndexedAccess, das beim Zugriff auf Arrays und Objekte mit undefined rechnet.

any vermeiden, unknown nutzen

any schaltet die Typprüfung für einen Wert komplett ab, und das ansteckend: Alles, was daraus abgeleitet wird, ist ebenfalls ungeprüft. Wo der Typ wirklich unbekannt ist, ist unknown die richtige Wahl. Der Compiler zwingt Sie dann, den Wert zu prüfen, bevor Sie ihn verwenden.

Unmögliche Zustände unmöglich machen

Ein häufiges Muster sind Objekte mit vielen optionalen Feldern, deren Kombinationen nicht alle gültig sind. Besser ist eine Discriminated Union:

type Request =
  | { status: "loading" }
  | { status: "success"; data: Order[] }
  | { status: "error"; message: string };

Jetzt kann es keinen Zustand geben, der gleichzeitig data und message hat. In einem switch über status weiß der Compiler in jedem Zweig, welche Felder vorhanden sind.

Daten an den Grenzen validieren

Typen existieren nur zur Entwicklungszeit. Was aus einer API, einem Formular oder einer Datenbank kommt, ist zur Laufzeit ungeprüft, egal, was die Typannotation behauptet. Bibliotheken wie Zod oder Valibot prüfen eingehende Daten gegen ein Schema und leiten daraus den TypeScript-Typ ab. So stimmen Typ und Wirklichkeit überein. Wie Sie Schnittstellen von vornherein klar definieren, beschreibt unser Leitfaden zu Full-Stack-Entwicklung mit TypeScript.

Lesbarkeit im Alltag

Namen, die erklären

Eine Funktion namens handleData sagt nichts. calculateShippingCost sagt, was passiert. Boolesche Werte lesen sich am besten als Frage: isPaid, hasAccess. Abkürzungen sparen beim Tippen ein paar Sekunden und kosten beim Lesen jedes Mal Zeit.

Kleine Funktionen mit einer Aufgabe

Eine Funktion, die Daten lädt, filtert, umrechnet und anzeigt, lässt sich schwer testen und schwer ändern. Teilen Sie sie entlang der Aufgaben. Als Faustregel: Wenn Sie zur Beschreibung einer Funktion „und“ brauchen, macht sie vermutlich zu viel.

Explizite Typen an öffentlichen Schnittstellen

Innerhalb einer Funktion leistet die Typinferenz gute Arbeit, explizite Annotationen sind dort oft Rauschen. An exportierten Funktionen und Modulgrenzen lohnen sich dagegen explizite Rückgabetypen. Sie dokumentieren den Vertrag und verhindern, dass eine interne Änderung unbemerkt die öffentliche Schnittstelle verändert.

Nach Fachlichkeit gliedern

Ordner wie components, utils und services wachsen in größeren Projekten zu Sammelbecken. Eine Gliederung nach fachlichen Bereichen, etwa orders, customers, billing, hält zusammen, was sich gemeinsam ändert.

Typische Fehler in TypeScript-Projekten

Der häufigste Fehler ist der großzügige Einsatz von as. Eine Type Assertion sagt dem Compiler „vertrau mir“, und genau dort entstehen später die Laufzeitfehler. Fast immer gibt es eine bessere Lösung: Validierung, Type Guards oder korrekt typisierte Funktionen.

Ebenso verbreitet sind übermäßig clevere Typen. Generische Konstruktionen über mehrere Ebenen beeindrucken, aber wenn niemand im Team sie versteht, sind sie eine Last. Typen sollen den Code verständlicher machen.

Und schließlich: Clean Code ohne Tests bleibt eine Behauptung. Erst Tests geben die Sicherheit, Code umzubauen, ohne Verhalten zu brechen. Welche Testarten sich wofür eignen, zeigt der Beitrag zu Teststrategien für React-Anwendungen. Wann sich Aufräumen wirtschaftlich lohnt, diskutieren wir in Technische Schulden in Startups.

Häufige Fragen (FAQ)

Lohnt sich der Strict Mode auch in einem bestehenden Projekt?

Ja, aber schrittweise. Aktivieren Sie die einzelnen Optionen nacheinander und beheben Sie die Meldungen modulweise. So bleibt das Projekt während der Umstellung lauffähig.

Sollte man in TypeScript eher type oder interface verwenden?

Beides ist in den meisten Fällen gleichwertig. Für Union Types und abgeleitete Typen brauchen Sie type, für erweiterbare Objektstrukturen ist interface üblich. Wichtiger als die Wahl ist eine einheitliche Regel im Team.

Welche Werkzeuge helfen, Clean Code durchzusetzen?

ESLint mit den TypeScript-Regeln, ein Formatter wie Prettier und eine CI-Pipeline, die Typprüfung, Linting und Tests bei jedem Pull Request ausführt. Stilfragen erledigt dann die Maschine, und das Review kann sich auf die Logik konzentrieren.

Fazit

Sauberer TypeScript-Code entsteht weniger durch Regeln aus Büchern als durch konsequente Nutzung des Typsystems und gute Gewohnheiten im Team. Wer Daten an den Grenzen prüft, auf any und as verzichtet und Code nach Fachlichkeit ordnet, spart sich einen großen Teil der Fehlersuche, die sonst später anfällt.

Wenn Sie ein TypeScript-Projekt aufsetzen oder ein bestehendes aufräumen möchten, sprechen Sie mit uns über Webentwicklung.