Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Events, Formulare und API-Antworten mit TypeScript typisieren

Events, Formulare und API-Antworten typisieren

~16 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Zum Abschluss der TypeScript-Integration: die drei Stellen, an denen TypeScript am meisten Bugs verhindert – Formular-Events (die berüchtigten "event.target.value ist vom Typ any"-Probleme), und Daten, die von AUSSERHALB der App kommen (die API), wo TypeScript naturgemäß NICHTS über die tatsächliche Struktur weiß, bis Sie es ihm sagen.

LoginPage.tsx: Formular-Events typisieren

src/pages/LoginPage.tsx
import { useState, FormEvent, ChangeEvent } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuthStore } from '../store/authStore';

function LoginPage() {
  const login = useAuthStore((state) => state.login);
  const [username, setUsername] = useState('');
  const navigate = useNavigate();

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    login(username || 'Jane Doe');
    navigate('/account');
  }

  function handleUsernameChange(event: ChangeEvent<HTMLInputElement>) {
    setUsername(event.target.value);
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>Log In</h2>
      <input
        type="text"
        placeholder="Username"
        value={username}
        onChange={handleUsernameChange}
      />
      <button type="submit">Log In</button>
    </form>
  );
}

export default LoginPage;

FormEvent<HTMLFormElement> und ChangeEvent<HTMLInputElement> sind React's generische Event-Typen, PARAMETRISIERT mit dem konkreten DOM-Element-Typ – GENAU DESHALB weiß TypeScript bei event.target.value, dass event.target ein HTMLInputElement ist (mit einer .value-Eigenschaft vom Typ string), nicht das generische, viel ärmer typisierte EventTarget. OHNE das Generic würde event.target.value einen Typfehler werfen – EventTarget selbst hat KEIN .value-Feld.

Die API-Grenze: magentoApi.ts typisieren

Der wichtigste, aber am leichtesten übersehene Punkt: TypeScript prüft nur Code, den ES SELBST analysieren kann. Eine fetch()-Antwort von einem externen Server ist zur KOMPILIER-Zeit reine Behauptung – TypeScript vertraut Ihnen, wenn Sie die Form der Antwort angeben, prüft aber NICHT zur Laufzeit, ob der Server tatsächlich hält, was Sie versprochen haben.

src/api/magentoApi.ts
import type { CartItem } from '../store/cartSlice';

export interface Product {
  sku: string;
  name: string;
  price: number;
  imageUrl: string;
}

interface ProductsResponse {
  items: Product[];
  totalCount: number;
}

interface MagentoApiItem {
  sku: string;
  name: string;
  price: number;
  image?: string;
}

function mapMagentoProduct(item: MagentoApiItem): Product {
  return {
    sku: item.sku,
    name: item.name,
    price: item.price,
    imageUrl: item.image ?? 'https://picsum.photos/300',
  };
}

export async function fetchProducts(page: number, pageSize: number): Promise<ProductsResponse> {
  const response = await fetch(
    `https://api.example.com/products?page=${page}&pageSize=${pageSize}`
  );

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const data = await response.json(); // Typ hier: 'any' - siehe Warnung unten
  return {
    items: data.products.map(mapMagentoProduct),
    totalCount: data.total,
  };
}

Achtung: await response.json() gibt IMMER any zurück – TypeScript kann unmöglich wissen, was ein entfernter Server tatsächlich schickt. Der Promise<ProductsResponse>-Rückgabetyp der Funktion ist eine BEHAUPTUNG Ihrerseits, keine automatisch verifizierte Garantie – TypeScript prüft nur, dass Ihr eigener Code intern KONSISTENT mit dieser Behauptung ist, nicht, dass der Server sie einhält. Für ECHTE Laufzeit-Validierung bräuchte man eine zusätzliche Bibliothek wie zod, die die Antwort tatsächlich zur Laufzeit gegen ein Schema prüft – über den Rahmen dieses Kapitels hinausgehend, aber ein wichtiger nächster Schritt für produktive Apps.

Warum das trotzdem wertvoll ist

Auch OHNE Laufzeit-Validierung bringt die Typisierung der API-Schicht enormen Wert: JEDER Aufrufer von fetchProducts() im restlichen Code bekommt jetzt eine korrekt typisierte Product[]-Liste zurück – Tippfehler wie product.pric (statt product.price) werden ÜBERALL, wo fetchProducts() verwendet wird, sofort erkannt, nicht erst, wenn die betroffene Zeile tatsächlich ausgeführt wird.

CartItem aus Kapitel 40 wiederverwenden

Beachten Sie import type {{ CartItem }} from '../store/cartSlice' oben in magentoApi.ts – wir importieren den in Kapitel 40 definierten Typ, statt ihn neu zu erfinden. Genau das ist der Sinn von export interface CartItem: EINE einzige Quelle der Wahrheit für "was ein Warenkorb-Artikel ist", von JEDER Datei im Projekt importierbar.

Tipp: Damit ist die Migration des Kernprojekts nach TypeScript abgeschlossen: cartSlice, store/index, ProductCard, authStore, LoginPage, magentoApi sind typisiert. Die verbleibenden Dateien (ProductListPage, App.jsx, CartPage, ...) folgen demselben Muster aus diesem und den letzten drei Kapiteln – eine gute Gelegenheit, das Gelernte SELBST anzuwenden, bevor es in Kapitel 44 mit automatisierten Tests weitergeht.