die pragmatische Migration ohne Big Bang
Ein bestehendes JavaScript Projekt komplett auf TypeScript umzustellen, klingt nach Wochen Stillstand. Wer TypeScript schrittweise einfuehrt, aktiviert allowJs, migriert Datei fuer Datei und erreicht am Ende strict Mode, ohne den Feature Betrieb jemals zu unterbrechen.
Inhaltsverzeichnis
- 1. Warum TypeScript schrittweise einfuehren statt Big Bang
- 2. Grundeinrichtung mit allowJs und checkJs
- 3. JSDoc Typen vor der eigentlichen Migration nutzen
- 4. Reihenfolge: welche Datei zuerst zu .tsx wird
- 5. Die erste Komponente zu TypeScript migrieren
- 6. any gezielt einsetzen statt zu verbieten
- 7. Custom Hooks und generische Typen
- 8. strict Mode als letzter Schritt aktivieren
- 9. Migrationsphasen im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum TypeScript schrittweise einfuehren statt Big Bang
Ein vollstaendiger Rewrite eines gewachsenen React Projekts von JavaScript zu TypeScript in einem einzigen Pull Request ist fuer die meisten Teams keine realistische Option. Wer TypeScript schrittweise einfuehren will, akzeptiert stattdessen eine Uebergangsphase, in der JavaScript und TypeScript Dateien nebeneinander existieren und gemeinsam kompiliert werden, ohne dass Feature Entwicklung pausiert werden muss.
Der TypeScript Compiler wurde genau fuer dieses Szenario gebaut. Mit der Option allowJs verarbeitet tsc sowohl .ts als auch .js Dateien im selben Projekt, und mit checkJs werden sogar JavaScript Dateien typgeprueft, ohne dass sie umbenannt werden muessen. Diese Kombination macht aus einer riskanten, monatelangen Migration ein kontinuierliches Hintergrundprojekt, das parallel zur normalen Entwicklung laeuft.
Der wirtschaftliche Grund fuer die schrittweise Migration ist ebenso wichtig wie der technische: ein Big Bang Rewrite bindet das gesamte Team fuer Wochen, ohne in dieser Zeit neue Fachlichkeit auszuliefern. Wer TypeScript schrittweise einfuehrt, liefert stattdessen jede Woche etwas messbaren Fortschritt, waehrend Features weiterhin wie gewohnt entwickelt werden.
2. Grundeinrichtung mit allowJs und checkJs
Der erste konkrete Schritt ist eine tsconfig.json, die bewusst permissiv konfiguriert ist. Strict Mode und viele andere strenge Optionen bleiben zunaechst deaktiviert, weil sie sofort Hunderte Fehler in unveraendertem JavaScript Code erzeugen wuerden. Ziel dieser ersten Phase ist lediglich, dass der Compiler ueberhaupt fehlerfrei durchlaeuft.
// tsconfig.json: deliberately permissive starting point
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"jsx": "react-jsx",
"allowJs": true,
"checkJs": false,
"strict": false,
"noImplicitAny": false,
"skipLibCheck": true,
"esModuleInterop": true,
"moduleResolution": "bundler"
},
"include": ["src"]
}
Wer TypeScript schrittweise einfuehrt, sollte checkJs zunaechst deaktiviert lassen und erst aktivieren, wenn die groebsten Probleme in einzelnen Dateien gezielt behoben wurden. Ein zu frueh aktiviertes checkJs flutet die IDE mit Warnungen fuer Code, der noch gar nicht Teil der aktuellen Migrationswelle ist, und demotiviert das Team unnoetig.
3. JSDoc Typen vor der eigentlichen Migration nutzen
Ein oft uebersehener Zwischenschritt, bevor man TypeScript schrittweise einfuehrt und Dateien tatsaechlich umbenennt, ist die Nutzung von JSDoc Typannotationen in reinem JavaScript. Der TypeScript Compiler versteht @param und @returns Kommentare bereits und nutzt sie fuer Autovervollstaendigung und Typpruefung, ganz ohne .ts Dateiendung.
// utils.js: JSDoc types give the TypeScript compiler information
// without renaming the file to .ts yet
/**
* @param {number} price
* @param {number} taxRate
* @returns {number}
*/
export function calculateGrossPrice(price, taxRate) {
return price * (1 + taxRate);
}
/**
* @typedef {Object} CartItem
* @property {string} id
* @property {number} price
* @property {number} quantity
*/
/**
* @param {CartItem[]} items
* @returns {number}
*/
export function calculateCartTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
Dieser Zwischenschritt hat einen praktischen Vorteil: er kostet fast nichts, laesst sich beilaeufig bei jeder ohnehin anstehenden Codeaenderung erledigen, und macht Funktionssignaturen bereits vor der eigentlichen Datei Migration sicherer. Teams, die TypeScript schrittweise einfuehren, gewinnen so schon Wochen vor der ersten umbenannten Datei messbaren Nutzen.
4. Reihenfolge: welche Datei zuerst zu .tsx wird
Fuer die Reihenfolge der eigentlichen Migration gilt eine klare Regel: Utility Funktionen und reine Datenstrukturen ohne UI zuerst, React Komponenten mit komplexem Props Interface und externen API Aufrufen zuletzt. Eine Utility Funktion wie formatCurrency hat wenige Abhaengigkeiten und laesst sich in Minuten typisieren, waehrend eine Formular Komponente mit React Hook Form und mehreren verschachtelten Feldern deutlich mehr Sorgfalt braucht.
Ein zweites Kriterium ist die Aenderungsfrequenz. Dateien, die staendig von mehreren Entwicklern gleichzeitig bearbeitet werden, sollten frueh migriert werden, weil TypeScript dort den groessten Nutzen bringt: Merge Konflikte, die durch falsche Datentypen entstehen, werden bereits beim Kompilieren sichtbar, statt erst zur Laufzeit.
5. Die erste Komponente zu TypeScript migrieren
Beim Uebertrag der ersten React Komponente wird aus der Datei Button.js die Datei Button.tsx, und Props bekommen ein explizites Interface statt impliziter Objektstruktur. Wer TypeScript schrittweise einfuehrt, sollte hier bewusst mit einer einfachen, praesentationsorientierten Komponente beginnen, nicht mit einer Container Komponente, die mehrere Datenquellen zusammenfuehrt.
// BEFORE: Button.js, props shape only documented in comments or nowhere at all
export function Button({ label, variant, onClick, disabled }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
// AFTER: Button.tsx, props shape enforced by the compiler
type ButtonVariant = 'primary' | 'secondary' | 'danger';
interface ButtonProps {
label: string;
variant: ButtonVariant;
onClick: () => void;
disabled?: boolean;
}
export function Button({ label, variant, onClick, disabled = false }: ButtonProps) {
return (
<button className={`btn btn-${variant}`} onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
6. any gezielt einsetzen statt zu verbieten
Ein haeufiges Missverstaendnis bei Teams, die TypeScript schrittweise einfuehren: any wird von Anfang an strikt verboten, was die Migration unnoetig verlangsamt. In der fruehen Phase ist ein bewusst gesetztes any an einer Schnittstelle zu noch nicht migriertem Code die richtige Wahl, solange es an einer klar sichtbaren Stelle steht und nicht heimlich durch fehlende Typannotationen entsteht.
Der Unterschied zwischen implizitem und explizitem any ist entscheidend. Ein implizites any, das durch fehlende Typisierung entsteht, versteckt sich und wird leicht uebersehen. Ein explizites data: any mit einem // TODO: type this once the API client is migrated Kommentar ist eine bewusste, nachvollziehbare Entscheidung, die spaeter gezielt aufgeloest werden kann. Ein grep -r ": any" im Projekt zeigt jederzeit den aktuellen Stand der noch offenen Stellen.
7. Custom Hooks und generische Typen
Custom Hooks profitieren besonders stark von TypeScript, weil generische Typen den Rueckgabewert praezise an die Aufrufstelle binden. Ein Hook wie useFetch<T> gibt je nach Verwendung unterschiedliche Datentypen zurueck, was in reinem JavaScript nur ueber Kommentare dokumentierbar war und in TypeScript direkt vom Compiler geprueft wird.
Wer TypeScript schrittweise einfuehrt und Custom Hooks migriert, sollte generische Typparameter nutzen, statt fuer jeden Anwendungsfall eine eigene, fast identische Hook Variante zu schreiben. Das reduziert Code Duplikation und macht die Typsicherheit an jeder Aufrufstelle sofort sichtbar, ohne dass die Dokumentation manuell gepflegt werden muss.
8. strict Mode als letzter Schritt aktivieren
Erst wenn der Grossteil der Codebasis migriert ist, sollte strict: true in der tsconfig.json aktiviert werden. Dieser Schritt aktiviert unter anderem strictNullChecks, was typischerweise die meisten neuen Fehler produziert, weil null und undefined ab diesem Zeitpunkt explizit behandelt werden muessen, statt stillschweigend akzeptiert zu werden.
Ein pragmatischer Zwischenschritt ist, strict Optionen einzeln statt gemeinsam zu aktivieren: zuerst noImplicitAny, dann strictNullChecks, erst danach die restlichen Optionen. So bleibt die Fehlerliste nach jeder Aktivierung ueberschaubar, statt bei einer einzigen grossen Umschaltung Hunderte neue Compiler Fehler gleichzeitig zu produzieren.
9. Migrationsphasen im direkten Vergleich
Die folgende Tabelle ordnet die einzelnen Phasen, in denen man TypeScript schrittweise einfuehrt, nach Aufwand und Nutzen ein.
| Phase | Aufwand | Nutzen | Typische Dauer |
|---|---|---|---|
| allowJs Setup | Sehr gering | Compiler laeuft, keine Fehler | Wenige Stunden |
| JSDoc Typen | Gering | Autovervollstaendigung ohne Umbenennung | Laufend, beilaeufig |
| Utility Funktionen migrieren | Mittel | Sichere Datenstrukturen im Kern | Ein bis zwei Wochen |
| Komponenten migrieren | Hoch | Props Vertraege compilergeprueft | Mehrere Monate |
| strict Mode aktivieren | Hoch, einmalig | Maximale Typsicherheit | Ein bis zwei Wochen am Ende |
Die Tabelle zeigt, dass die fruehen Phasen der Migration den besten Aufwand zu Nutzen Verhaeltnis bieten. Wer TypeScript schrittweise einfuehrt, sollte deshalb bewusst mit den guenstigen Phasen beginnen und sich den teuren strict Mode Schritt fuer das Ende aufheben, wenn der Grossteil der Arbeit bereits erledigt ist.
Mironsoft
React TypeScript Migrationen und Type Safety Beratung
Noch ein reines JavaScript React Projekt ohne Typsicherheit?
Wir planen die schrittweise TypeScript Einfuehrung fuer euer Projekt, priorisieren Dateien nach Risiko und begleiten den Weg bis zum vollstaendigen strict Mode.
Migrations Setup
tsconfig.json mit allowJs und schrittweisem Strictness Plan
Begleitete Migration
Datei fuer Datei Uebertrag ohne Unterbrechung der Feature Arbeit
strict Mode Rollout
Kontrollierte Aktivierung einzelner strict Optionen
10. Zusammenfassung
Wer TypeScript schrittweise einfuehren will, beginnt mit einer permissiven tsconfig.json mit allowJs, nutzt JSDoc Typen als kostenguenstigen Zwischenschritt und migriert dann Utility Funktionen vor komplexen Komponenten. Bewusst gesetztes any an klar sichtbaren Stellen ist waehrend der Migration kein Problem, solange es dokumentiert und spaeter aufgeloest wird.
Custom Hooks profitieren besonders von generischen Typen, und strict Mode wird erst am Ende schrittweise aktiviert, wenn der Grossteil der Codebasis bereits typisiert ist. Diese Reihenfolge macht aus einer riskanten, monatelangen Migration ein kontinuierliches Hintergrundprojekt, das den Feature Betrieb zu keinem Zeitpunkt unterbricht.
TypeScript schrittweise einfuehren: Das Wichtigste auf einen Blick
Startpunkt
allowJs und checkJs erlauben JavaScript und TypeScript nebeneinander im selben Projekt.
Reihenfolge
Utility Funktionen zuerst, komplexe Komponenten mit vielen Abhaengigkeiten zuletzt.
any bewusst nutzen
Explizites any mit TODO Kommentar statt verstecktem implizitem any.
strict Mode am Ende
Einzelne strict Optionen nacheinander aktivieren statt alles auf einmal.