Hirefullstack – Software Engineering & IT-Beratung aus Berlin
← Zur Übersicht
Einstieg 11 Min Lesezeit

Props und Events typisieren

Wo im React-Alltag Typen hingehören – und wo nicht

In einem Satz

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.

Button.tsx
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>;
}
Gut zu wissen React.FC brauchst du nicht

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

children.tsx
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

events.tsx
// 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");
}
Tipp Der einfachste Weg

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

hooks.tsx
// 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);
Stolperstein `useState([])` geht schief

Aus einem leeren Array kann TypeScript nicht ableiten, was darin liegen soll. Danach ist jedes setItems(["a"]) ein Fehler. Schreib es hin: useState<string[]>([]).

Nimm es, wenn …
  • +

    Props – das ist der Vertrag deiner Komponente

  • +

    Ausgelagerte Event-Handler

  • +

    useState mit leerem Array oder null als Startwert

  • +

    Alles, was von einer API oder Bibliothek kommt

Lass es, wenn …
  • Rückgabetypen von Komponenten (: JSX.Element) – wird sowieso erraten

  • Lokale Variablen, die ohnehin klar sind

  • any als 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.

jede Prop einzeln nachbauen
type Props = {
  label: string;
  onClick: () => void;
  disabled?: boolean;
  type?: "button" | "submit";
  className?: string;
  // … und die anderen 40?
};
vom echten Element erben
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

List.tsx
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

Eine typsichere Komponente bauen

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 Text Neu
  • ton="danger" setzt data-ton="danger", Standard ist neutral
  • Ein mitgegebenes title landet 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>
  );
}

Hirefullstack

Ihr braucht React-Verstärkung im Team?

Wir bauen seit Jahren React- und Next.js-Anwendungen für Kunden in ganz Deutschland – als einzelner Experte, als Verstärkung fürs Bestandsteam oder als komplettes Scrum-Team.

Projekt besprechen →