← Alle Artikel

Strict Mode in TypeScript: Migration ohne Schockstarre

Du willst TypeScript strict mode in deiner gewachsenen Codebase einführen? Erfahre, wie du inkrementell vorgehst, welche Flags zuerst anpackst und dein…

TypeScriptCode-QualitätMigrationWeb-DevBest Practices

TypeScript im strict mode zu nutzen, ist eine der besten Entscheidungen für die Code-Qualität deines Projekts. Aber seien wir ehrlich: Eine gewachsene Codebase auf strict umzustellen, kann sich wie ein riesiger Berg anfühlen. Die gute Nachricht: Du musst nicht alles auf einmal machen. Mit einem inkrementellen Ansatz und den richtigen Prioritäten kannst du dein Team mitnehmen und die Vorteile schrittweise nutzen.

Warum der strict mode dein Freund ist

Du kennst es sicher: Ein Bug taucht auf, weil null oder undefined an einer Stelle auftaucht, wo es niemand erwartet hat. Oder du bekommst ein any reingereicht, weil der Typ von irgendwoher nicht inferiert werden konnte, und plötzlich verlierst du die Typsicherheit genau da, wo du sie am dringendsten brauchst. Der TypeScript strict mode ist genau dafür da, solche Fallstricke von vornherein zu vermeiden. Er erzwingt strengere Typüberprüfungen und hilft dir, Fehler schon während der Entwicklung zu finden, statt erst zur Laufzeit.

Aus eigener Erfahrung in diversen Agenturprojekten und bei der Entwicklung unseres eigenen SaaS kann ich sagen: Auch wenn es am Anfang nach mehr Arbeit aussieht, spart der strict mode langfristig massiv Zeit und Nerven. Weniger Bugs, bessere Wartbarkeit, leichtere Refactorings und ein klareres Verständnis des Codes sind die unmittelbaren Vorteile. Deine Entwickler:innen werden es dir danken, wenn sie sich nicht mehr durch unsicheren Code kämpfen müssen.

Die Angst vor der großen Migration: Wie man sie vermeidet

Der Gedanke, einen Schalter umzulegen und hunderte von Kompilierungsfehlern zu bekommen, lähmt viele Teams. Das ist verständlich. Ein “Big Bang” ist hier der falsche Ansatz. Stattdessen brauchst du eine Strategie, die den Aufwand verteilt und den Nutzen schnell sichtbar macht. Es geht darum, das Team nicht zu überfordern und die Migration als kontinuierlichen Prozess zu etablieren, nicht als einmaliges, schmerzhaftes Event.

Der Schlüssel liegt darin, die einzelnen strict-Flags nicht als unteilbares Ganzes zu sehen, sondern sie einzeln und gezielt zu aktivieren. Jedes Flag hat einen spezifischen Fokus und kann unterschiedlich viel Aufwand bedeuten. Lass uns die wichtigsten im Detail betrachten.

Inkremenelle Migration: Welche Flags zuerst anpacken?

Die strict-Option in deiner tsconfig.json ist eigentlich nur ein Alias für eine ganze Reihe von einzelnen Flags. Um nicht in der Schockstarre zu verharren, deaktivieren wir strict: true und aktivieren die einzelnen Optionen stattdessen schrittweise. Hier ist ein sinnvoller Weg, wie du vorgehen kannst, um den größtmöglichen Nutzen mit überschaubarem Aufwand zu erzielen.

1. noImplicitAny – Der erste, oft schmerzhafte, aber notwendige Schritt

{
  "compilerOptions": {
    "noImplicitAny": true
  }
}

noImplicitAny ist wahrscheinlich das Flag, das dir zuerst die meisten Fehler bescheren wird, aber auch das größte Potenzial zur Verbesserung der Code-Qualität birgt. Es verhindert, dass TypeScript stillschweigend any annimmt, wenn der Typ nicht inferiert werden kann. Das zwingt dich (und dein Team), explizite Typen anzugeben.

Warum zuerst? Auch wenn es wehtut: any ist der größte Feind der Typsicherheit. Wenn du neue Features entwickelst, erzwingt dieses Flag sofort saubere Typen. Für bestehenden Code kannst du es vorübergehend mit // @ts-ignore oder // @ts-expect-error umgehen, aber nur als Übergangslösung. Ideal ist es, dieses Flag für neuen Code sofort zu aktivieren und dann sukzessive Altcode anzupassen.

2. noUnusedLocals und noUnusedParameters – Schnelle Gewinne

{
  "compilerOptions": {
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

Diese beiden Flags sind oft relativ einfach zu aktivieren und bieten sofort sichtbare Verbesserungen. Sie warnen dich, wenn lokale Variablen oder Parameter deklariert, aber nie verwendet werden. Das hilft, toten Code zu finden und aufzuräumen.

Warum früh? Sie sind schnelle Wins. Die Fehler lassen sich meist mit einem Knopfdruck deiner IDE beheben und verbessern sofort die Lesbarkeit und Wartbarkeit deines Codes. Dein Team sieht schnell positive Effekte der Migration.

3. strictNullChecks – Der Game Changer

{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

Dieses Flag ist ein absoluter Game Changer und nach noImplicitAny das wichtigste. Es verhindert, dass du unbeabsichtigt null oder undefined verwendest, wo ein Wert erwartet wird. Das ist die Ursache für unzählige Laufzeitfehler und eine der größten Stärken von TypeScript.

Warum jetzt? Es ist oft der zweitgrößte Brocken nach noImplicitAny. Wenn du aber schon noImplicitAny angegangen bist, ist der Weg geebnet. Die Fehler sind oft null oder undefined Überprüfungen, die fehlen. Das ist aufwendig, aber extrem wertvoll. Beginne auch hier mit neuem Code und arbeite dich dann durch die bestehenden Stellen. Setze ? für optionale Eigenschaften oder ! für Non-Null Assertion ein, aber nutze ! sparsam und bewusst.

4. noImplicitReturns – Vollständigkeit bei Funktionen

{
  "compilerOptions": {
    "noImplicitReturns": true
  }
}

Dieses Flag stellt sicher, dass alle Codepfade in einer Funktion einen Wert zurückgeben, wenn die Funktion einen Rückgabetyp erwartet. Das fängt Logikfehler ab, bei denen du in bestimmten Szenarien vergessen hast, etwas zurückzugeben.

Warum später? Es ist weniger invasiv als strictNullChecks und noImplicitAny, aber immer noch wichtig für die Konsistenz und Korrektheit deiner Funktionen.

5. strictFunctionTypes – Strengere Funktionssignaturen

{
  "compilerOptions": {
    "strictFunctionTypes": true
  }
}

Dieses Flag wendet strengere Regeln an, wenn Funktionen verglichen werden. Es ist besonders wichtig, um subtile Fehler zu vermeiden, wenn du Funktionen als Argumente übergibst oder Callback-Funktionen verwendest. Es verbessert die Typsicherheit von Callbacks und Event-Handlern erheblich.

Warum später? Es kann bei komplexeren Funktionssignaturen zu unerwarteten Fehlern führen, deren Behebung etwas Denkarbeit erfordert. Aber es ist ein wichtiger Schritt hin zu einer vollständig typsicheren Codebase.

6. strictPropertyInitialization – Klassen-Eigenschaften sicher initialisieren

{
  "compilerOptions": {
    "strictPropertyInitialization": true
  }
}

Dieses Flag stellt sicher, dass Klassen-Eigenschaften, die nicht als optional (?) oder mit dem definite assignment assertion (!) markiert sind, im Konstruktor initialisiert werden. Das verhindert, dass du auf uninitialisierte Eigenschaften zugreifst.

Warum später? Es ist spezifisch für Klassen und kann bei bestehenden Klassen, die nicht sauber initialisiert wurden, zu Anpassungen führen. Es ist aber entscheidend für die Sicherheit objektorientierten Codes.

7. alwaysStrict – JavaScript Strict Mode erzwingen

{
  "compilerOptions": {
    "alwaysStrict": true
  }
}

Dieses Flag sorgt dafür, dass deine JavaScript-Dateien im strict mode ausgegeben werden. Es ist weniger eine Typ-Prüfung als eine Laufzeit-Erzwingung von sauberem JavaScript. Es ist meist unproblematisch, da moderner JavaScript-Code ohnehin im Strict Mode läuft.

Warum ganz zum Schluss? Es ist meist eine Formsache und hat kaum Auswirkungen auf die Typ-Prüfung selbst. Du kannst es aktivieren, sobald die anderen, wichtigeren Flags stabil sind.

Praktische Tipps für die Migration

Die Migration ist kein Sprint, sondern ein Marathon. Hier sind ein paar Dinge, die dir und deinem Team helfen, motiviert zu bleiben und effektiv voranzukommen:

Team Buy-in und Kommunikation

Der wichtigste Faktor neben der technischen Umsetzung ist dein Team. Erkläre immer wieder den Nutzen der Umstellung. Zeige auf, wie strict mode die Code-Qualität verbessert und die Fehlerquote senkt. Feiert kleine Erfolge! Jedes behobene any oder jede neue null-Prüfung ist ein Schritt in die richtige Richtung.

Sei ehrlich beim Aufwand. Ja, es wird zusätzliche Arbeit bedeuten. Aber diese Arbeit zahlt sich direkt in weniger Stress und höherer Entwicklerzufriedenheit aus. Wenn das Team den Wert erkennt, wird es die Migration als gemeinsame Aufgabe annehmen.

Aufwand und Vorteile: Eine ehrliche Einschätzung

Der Aufwand für eine vollständige Migration kann beträchtlich sein, besonders bei sehr alten oder großen Codebasen. Rechne je nach Größe des Projekts und dem Zustand des Codes mit mehreren Wochen bis Monaten, verteilt auf kleine Aufgaben. Es ist selten ein Vollzeitjob, sondern eher eine kontinuierliche Verbesserung.

Die Vorteile überwiegen jedoch bei Weitem. Du wirst eine deutlich stabilere Anwendung haben, die einfacher zu warten und zu erweitern ist. Bugs, die auf Typunsicherheiten zurückzuführen sind, werden drastisch reduziert. Dein Team wird sich sicherer fühlen, Änderungen vorzunehmen, und die Onboarding-Zeit für neue Entwickler:innen sinkt, weil der Code selbsterklärender ist.

Fazit

Die Umstellung auf den TypeScript strict mode ist eine Investition, die sich auszahlt. Gehe es inkrementell an, konzentriere dich auf die Flags, die den größten Nutzen versprechen, und nimm dein Team mit auf die Reise. Die verbesserte Code-Qualität und die reduzierte Fehleranfälligkeit sind den Aufwand wert. Fang klein an, bleib dran und genieße die Vorteile einer robusteren Codebase. Wenn du dich mit anderen Entwickler:innen über solche Themen austauschen möchtest, schau doch mal im Discord der Community vorbei!