TypeScript schrittweise in React einfuehren: die Migration
AI generated
</>
{ }
React · TypeScript · Migration · Type Safety
TypeScript schrittweise in React einfuehren
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.

17 Min. Lesezeit allowJs · JSDoc · strict Mode TypeScript 5.x · React 19

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.

11. FAQ: TypeScript schrittweise einfuehren

1Muss ich alles gleichzeitig migrieren?
Nein, allowJs erlaubt JS und TS beliebig lange nebeneinander.
2Was bringt JSDoc vorab?
Gibt dem Compiler Typinformationen ohne Datei Umbenennung, kostet fast nichts.
3In welcher Reihenfolge migrieren?
Utility Funktionen zuerst, komplexe Komponenten zuletzt.
4Ist any waehrend der Migration erlaubt?
Ja, explizit mit TODO Kommentar, solange nicht heimlich implizit.
5Wann strict Mode aktivieren?
Am Ende, schrittweise Option fuer Option statt alles gleichzeitig.
6Wie profitieren Custom Hooks?
Generische Typen binden den Rueckgabewert praezise an die Aufrufstelle.
7Muss der Feature Betrieb pausieren?
Nein, genau das ist der Vorteil der schrittweisen Migration.
8Unterschied allowJs und checkJs?
allowJs verarbeitet JS Dateien, checkJs prueft sie zusaetzlich auf Typfehler.
9Wie finde ich verbleibende any Stellen?
grep -r ": any" im src Ordner zeigt alle expliziten Annotationen.
10Lohnt sich TypeScript fuer kleine Projekte?
Bei sehr kleinen Solo Projekten oft nicht, bei mehreren Entwicklern fast immer.