Props und Events typisieren
Wo im React-Alltag Typen hingehören – und wo nicht
In React schreibst du fast nur an drei Stellen Typen hin: Props, ausgelagerte Event-Handler und ein paar Hooks. Den Rest errät TypeScript.
Props: ein Objekt, ein Typ
Eine KomponenteEine Funktion, die beschreibt, wie ein Stück Oberfläche aussehen soll – zum Beispiel ein Button oder eine ganze Seite.Sie beginnt immer mit einem Großbuchstaben und gibt JSX zurück. Du benutzt sie dann wie ein eigenes HTML-Element: <MeinButton />.→ bekommt ein Objekt herein. Also beschreibst du genau dieses Objekt – mehr nicht.
type ButtonProps = {
label: string;
variante?: "primary" | "ghost"; // ? = optional
onClick: () => void; // Funktion ohne Argumente, ohne Rückgabe
};
export function Button({ label, variante = "primary", onClick }: ButtonProps) {
return <button className={variante} onClick={onClick}>{label}</button>;
} Früher schrieb man const X: React.FC<Props> = …. Heute ist die schlichte Funktion mit typisiertem Parameter der Standard – kürzer und ohne Überraschungen.
children: alles, was dazwischen steht
import type { ReactNode } from "react";
type CardProps = {
titel: string;
children: ReactNode; // alles Anzeigbare: JSX, Text, Zahl, Array, null
};
export function Card({ titel, children }: CardProps) {
return (
<section>
<h2>{titel}</h2>
{children}
</section>
);
}
// Benutzung:
<Card titel="Hinweis">Das hier landet in children.</Card> ReactNode ist fast immer richtig. JSX.Element ist enger – damit wäre schon <Card>Hallo</Card> ein Fehler, weil einfacher Text kein Element ist.
Events: den Typ vom Element her denken
// Direkt hingeschrieben: TypeScript weiß von selbst Bescheid – bevorzugt!
<input onChange={(e) => console.log(e.target.value)} />
// Ausgelagert: jetzt musst du den Typ selbst hinschreiben
import type { ChangeEvent, FormEvent, MouseEvent } from "react";
function handleChange(e: ChangeEvent<HTMLInputElement>) {
console.log(e.target.value);
}
function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
}
function handleClick(e: MouseEvent<HTMLButtonElement>) {
console.log("geklickt");
} Schreib den Handler zuerst direkt ins JSX und lass dir vom Editor den Typ anzeigen. Danach kannst du ihn herausziehen und den Typ abschreiben – schneller, als ihn zu raten.
Hooks: nur wo nötig
// Meistens reicht der geratene Typ:
const [count, setCount] = useState(0); // number
const [name, setName] = useState(""); // string
// Nötig, sobald der Startwert nicht alles abdeckt:
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<string[]>([]);
// Bei einer Ref aufs DOM:
const inputRef = useRef<HTMLInputElement>(null); Aus einem leeren Array kann TypeScript nicht ableiten, was darin liegen soll. Danach ist jedes setItems(["a"]) ein Fehler. Schreib es hin: useState<string[]>([]).
- +
Props – das ist der Vertrag deiner Komponente
- +
Ausgelagerte Event-Handler
- +
useStatemit leerem Array odernullals Startwert - +
Alles, was von einer API oder Bibliothek kommt
- −
Rückgabetypen von Komponenten (
: JSX.Element) – wird sowieso erraten - −
Lokale Variablen, die ohnehin klar sind
- −
anyals Lückenfüller
▸ Genauer erklärt: Props erben und eigene Bausteine flexibel machen optional
Native Attribute übernehmen statt abschreiben
Deine <Button>-Komponente soll auch disabled, title, aria-label und den Rest können? Dann musst du die nicht alle abtippen.
type Props = {
label: string;
onClick: () => void;
disabled?: boolean;
type?: "button" | "submit";
className?: string;
// … und die anderen 40?
}; import type { ComponentProps } from "react";
type Props = ComponentProps<"button"> & {
label: string;
};
export function Button({ label, ...rest }: Props) {
return <button {...rest}>{label}</button>;
} ComponentProps<"button"> liefert alle Attribute, die ein echtes <button> annimmt. Für eigene Komponenten geht das genauso: ComponentProps<typeof Card>.
Eine Komponente für beliebige Daten
type ListProps<T> = {
items: T[];
zeige: (item: T) => ReactNode;
schluessel: (item: T) => string;
};
export function List<T>({ items, zeige, schluessel }: ListProps<T>) {
return <ul>{items.map((i) => <li key={schluessel(i)}>{zeige(i)}</li>)}</ul>;
}
// Beim Benutzen wird T automatisch bestimmt:
<List
items={users} // T = User
schluessel={(u) => u.id} // u ist User
zeige={(u) => u.name} // u ist User
/> Sitzt das schon?
4 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.
Jetzt selbst schreiben
Vervollständige Badge: Der Text kommt als children herein, ton färbt sie ein. Zusätzlich soll sie normale <span>-Attribute wie title durchreichen.
- □
<Badge>Neu</Badge>zeigt den TextNeu - □
ton="danger"setztdata-ton="danger", Standard istneutral - □Ein mitgegebenes
titlelandet am<span>
import type { ComponentProps } from "react"; // TODO: Props typisieren (children + ton + normale span-Attribute) export function Badge(props: any) { return <span>???</span>; } export default function App() { return ( <div style={{ fontFamily: "system-ui", padding: 16, display: "flex", gap: 8 }}> <Badge>Neu</Badge> <Badge ton="danger" title="Achtung">Fehler</Badge> </div> ); }