TypeScript zum React-Projekt hinzufügen
TypeScript zum Projekt hinzufügen
~14 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Ab hier migrieren wir produktkatalog-web schrittweise zu TypeScript. WICHTIG: Dieses und die nächsten drei Kapitel zeigen, WIE man TypeScript MIT React kombiniert – Props typisieren, Hooks typisieren, Events typisieren. Die TypeScript-SPRACHE selbst (jedes Feature von Grund auf, interface vs. type, Generics, Utility-Types, ...) wird sehr ausführlich in einem eigenen, separaten TypeScript-Tutorial behandelt – hier setzen wir grundlegende TypeScript-Kenntnisse voraus und konzentrieren uns auf die React-INTEGRATION.
Warum TypeScript für ein React-Projekt lohnt
Erinnern Sie sich an Bugs wie "product.price.toFixed ist keine Funktion", weil price unerwartet ein String statt einer Zahl war? Oder an einen Tippfehler in einem Prop-Namen (onAddToCard statt onAddToCart), der erst beim Testen im Browser auffiel? TypeScript fängt beide Fehlerklassen bereits WÄHREND des Tippens im Editor ab, VOR dem ersten Ausführen.
Vite-Projekt auf TypeScript umstellen
Vite unterstützt TypeScript nativ – keine separate Build-Pipeline nötig, nur die richtigen Pakete und Dateiendungen. Installieren Sie die TypeScript-Typdefinitionen für React:
npm install --save-dev typescript @types/react @types/react-domtsconfig.json anlegen
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}"noEmit": true– TypeScript wird hier NUR zur Typprüfung genutzt, nicht zum Kompilieren; Vite/esbuild übernehmen das eigentliche Kompilieren (und entfernen die Typen dabei einfach)."jsx": "react-jsx"– aktiviert die moderne JSX-Transformation, die keinimport React from 'react'mehr in jeder Datei benötigt (identisch zu Vites JavaScript-Voreinstellung)."strict": true– aktiviert ALLE strengen Typprüfungen auf einmal (u. a.strictNullChecks, ohne dasnull/undefinedpraktisch überall erlaubt wären) – für ein neues Projekt IMMER empfohlen, nachträgliches Aktivieren in einer großen bestehenden Codebase ist deutlich schmerzhafter.
Die erste Datei umstellen: utils/bmi.js-Äquivalent hier cartSlice.js
TypeScript-Dateien mit JSX-Syntax bekommen die Endung .tsx, reine Logikdateien ohne JSX .ts. Beginnen wir mit einer Datei OHNE JSX – src/store/cartSlice.js wird zu cartSlice.ts:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export interface CartItem {
sku: string;
name: string;
price: number;
quantity: number;
}
interface CartState {
items: CartItem[];
}
const initialState: CartState = { items: [] };
const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
addItem(state, action: PayloadAction>) {
const { sku, name, price } = action.payload;
const existing = state.items.find((item) => item.sku === sku);
if (existing) {
existing.quantity += 1;
} else {
state.items.push({ sku, name, price, quantity: 1 });
}
},
removeItem(state, action: PayloadAction<string>) {
state.items = state.items.filter((item) => item.sku !== action.payload);
},
updateQuantity(state, action: PayloadAction<{ sku: string; quantity: number }>) {
const { sku, quantity } = action.payload;
const item = state.items.find((item) => item.sku === sku);
if (item) {
item.quantity = Math.max(1, quantity);
}
},
clearCart(state) {
state.items = [];
},
},
});
export const { addItem, removeItem, updateQuantity, clearCart } = cartSlice.actions;
export default cartSlice.reducer; export interface CartItem ist der wichtigste neue Baustein: eine wiederverwendbare Typ-Definition, die wir in den nächsten Kapiteln in MEHREREN Dateien importieren werden (ProductCard, CartPage, ...) – EIN einziger Ort, der beschreibt, "was ein Warenkorb-Artikel ist". PayloadAction<T> ist Redux Toolkits eigener generischer Typ für Actions – T beschreibt die Form von action.payload. Omit<CartItem, 'quantity'> nimmt CartItem und ENTFERNT das quantity-Feld – exakt passend zu unserem addItem-Aufruf, der niemals eine Menge übergibt (die wird intern immer auf 1 gesetzt oder erhöht).
src/store/index.js zu index.ts
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from './cartSlice';
export const store = configureStore({
reducer: {
cart: cartReducer,
},
});
// Aus dem Store SELBST abgeleitete Typen - müssen nie manuell synchron gehalten werden,
// da sie sich automatisch an Änderungen im Store anpassen:
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;ReturnType<typeof store.getState> ist ein klassisches TypeScript-Muster: statt RootState von Hand nachzupflegen (fehleranfällig, gerät leicht außer Synchronisation), wird der Typ AUTOMATISCH aus der tatsächlichen Store-Konfiguration ABGELEITET. RootState/AppDispatch werden im nächsten Kapitel für typsichere useSelector/useDispatch-Aufrufe gebraucht.
Achtung: Ab jetzt können .js/.jsx- und .ts/.tsx-Dateien im selben Projekt KOEXISTIEREN – die Migration muss nicht auf einmal passieren. Genau so gehen wir in den nächsten Kapiteln vor: Datei für Datei umstellen, nicht alles gleichzeitig.