TSX und der Aufstieg typisierter Frontend-Komponenten
Sie erstellen eine React-Komponente. Sie übergeben einen String, wo eine Zahl hingehört. TypeScript erkennt dies, bevor es der Browser tut. Diese einfache Interaktion – Typen, die Laufzeitfehler verhindern – erklärt, warum TSX zum Standardformat für moderne Frontend-Entwicklung geworden ist.
Dieser Artikel behandelt, was typisierte Frontend-Komponenten in der Praxis tatsächlich bedeuten: Props-Typisierung, Event-Handling, Children-Patterns und wie TypeScript-Inferenz Boilerplate eliminiert. Wir gehen auch darauf ein, wie die Aufteilung von Server- und Client-Komponenten in React 19 Ihre Typisierungsstrategie beeinflusst.
Wichtigste Erkenntnisse
- TSX-typisierte Komponenten erkennen Typ-Inkompatibilitäten zur Kompilierzeit und reduzieren Laufzeitfehler, bevor Code den Browser erreicht.
- TypeScript-Inferenz behandelt die meiste Event-Typisierung automatisch – explizite Annotationen sind nur erforderlich, wenn Handler in separate Funktionen extrahiert werden.
- Discriminated Unions ermöglichen typsichere Komponenten mit sich gegenseitig ausschließenden Verhaltensweisen, bei denen TypeScript Typen basierend auf einer Diskriminanten-Eigenschaft eingrenzt.
- Die Server-/Client-Grenze in React 19 führt Serialisierungsbeschränkungen ein: Props, die von Server- zu Client-Komponenten übergeben werden, müssen serialisierbar sein (keine Funktionen, Klassen, Symbole oder andere nicht-serialisierbare Werte).
Was TSX-typisierte Komponenten tatsächlich bedeuten
Eine typisierte Komponente ist nicht nur eine Komponente mit typisierten Props. Es ist eine Komponente, bei der TypeScript den gesamten Vertrag versteht: was hineingeht, was herauskommt und was dazwischen passiert.
Mit der modernen JSX-Transformation (react-jsx) importieren Sie React nicht mehr in jeder Datei. Sie schreiben TSX, und die Toolchain kümmert sich um den Rest. Vite konfiguriert dies standardmäßig korrekt:
interface ButtonProps {
label: string
disabled?: boolean
}
function Button({ label, disabled = false }: ButtonProps) {
return <button disabled={disabled}>{label}</button>
}
Beachten Sie, dass hier kein React.FC verwendet wird. Dieses Pattern ist optional – und oft unnötig. Die direkte Typisierung von Props am Funktionsparameter ist sauberer und vermeidet die implizite children-Prop, die React.FC hinzufügt, unabhängig davon, ob Sie sie wollen oder nicht.
Props-Typisierung und React TSX Type Safety
Props-Typisierung ist der Einstiegspunkt für die meisten Entwickler, aber React TSX Type Safety geht tiefer. Betrachten Sie Discriminated Unions für Komponenten mit sich gegenseitig ausschließenden Verhaltensweisen:
type LinkButtonProps =
| { variant: 'button'; onClick: () => void }
| { variant: 'link'; href: string }
function LinkButton(props: LinkButtonProps) {
if (props.variant === 'link') {
return <a href={props.href}>Navigate</a>
}
return <button onClick={props.onClick}>Click</button>
}
TypeScript grenzt den Typ basierend auf variant ein. Sie können nicht versehentlich auf href zugreifen, wenn variant den Wert 'button' hat. Dieses Pattern skaliert gut für komplexe Komponenten-APIs.
Event-Typisierung ohne Boilerplate
Event-Handler sind oft eine Stolperfalle für Entwickler, die neu bei TypeScript sind. Die gute Nachricht: Inferenz behandelt die meisten Fälle automatisch.
import { useState } from 'react'
function SearchInput() {
const [query, setQuery] = useState('')
// TypeScript leitet den Event-Typ aus dem Kontext ab
return (
<input
value={query}
onChange={(e) => setQuery(e.currentTarget.value)}
/>
)
}
Wenn Sie Handler in separate Funktionen extrahieren, benötigen Sie explizite Typen:
import type { ChangeEvent } from 'react'
function handleChange(event: ChangeEvent<HTMLInputElement>) {
// event.currentTarget.value ist korrekt als string typisiert
}
Discover how at OpenReplay.com.
Children-Typisierung: ReactNode vs ReactElement
Für Children deckt React.ReactNode den gängigen Fall ab – alles, was renderbar ist:
import type { ReactNode } from 'react'
interface CardProps {
children: ReactNode
}
Verwenden Sie React.ReactElement, wenn Sie strikt JSX-Elemente benötigen und Strings und Zahlen ausschließen möchten. Für Komponenten, die Children akzeptieren, ist PropsWithChildren eine sauberere Alternative zum manuellen Hinzufügen der children-Prop:
import type { PropsWithChildren } from 'react'
interface CardProps {
title: string
}
function Card({ title, children }: PropsWithChildren<CardProps>) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
)
}
Typisierung von Server- und Client-Komponenten in React 19
React-19-TypeScript-Projekte, insbesondere solche, die den Next.js App Router verwenden, teilen Komponenten in Server- und Client-Grenzen auf. Dies beeinflusst die Typisierung.
Server-Komponenten können asynchron sein und Daten direkt abrufen:
// Server Component - keine Direktive erforderlich (Standard im App Router)
async function UserProfile({ userId }: { userId: string }) {
const user = await fetchUser(userId)
return <div>{user.name}</div>
}
Client-Komponenten erfordern die 'use client'-Direktive und können Hooks verwenden:
'use client'
import { useState } from 'react'
interface CounterProps {
initialCount: number
}
function Counter({ initialCount }: CounterProps) {
const [count, setCount] = useState(initialCount)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
Die Typisierungsgrenze ist wichtig: Props, die von Server- zu Client-Komponenten übergeben werden, müssen serialisierbar sein. Funktionen, Klassen, Symbole und nicht-serialisierbare Werte (wie Klasseninstanzen oder Dates) können diese Grenze nicht überschreiten.
Form Actions und moderne typisierte Workflows
React 19 führt Server Actions ein (häufig mit Formularen verwendet) als typisiertes Pattern für die Verarbeitung von Übermittlungen, die typischerweise in Server-Dateien definiert und von Client-Komponenten aufgerufen werden:
async function submitForm(formData: FormData) {
'use server'
const email = formData.get('email')
if (typeof email !== 'string') {
throw new Error('Email is required')
}
// Übermittlung verarbeiten
}
Dies integriert sich mit typisierten Frontend-Komponenten, indem die Formularverarbeitung vom Client zum Server typsicher bleibt.
Fazit
TSX-typisierte Komponenten reduzieren Bugs, indem sie Inkompatibilitäten zur Kompilierzeit erkennen. TypeScript-Inferenz minimiert Boilerplate – Sie benötigen selten explizite Typ-Annotationen für Hooks oder Inline-Handler. Die Server-/Client-Aufteilung in React 19 fügt eine neue Überlegung hinzu: Serialisierungsgrenzen.
Verzichten Sie auf React.FC, es sei denn, Sie haben einen spezifischen Grund. Typisieren Sie Props direkt. Lassen Sie Inferenz arbeiten. Ihr Editor wird es Ihnen mit präziser Autovervollständigung und sofortigem Fehler-Feedback danken.
FAQs
Typisieren Sie Props direkt an Funktionsparametern. React.FC fügt eine implizite children-Prop hinzu, unabhängig davon, ob Sie sie wollen oder nicht, und die direkte Typisierung von Props ist sauberer. Das React-Team empfiehlt React.FC nicht als Standard-Pattern. Verwenden Sie es nur, wenn Sie sein Verhalten speziell benötigen, beispielsweise bei der Arbeit mit Legacy-Codebasen, die es erwarten.
Für Inline-Handler leitet TypeScript den Event-Typ automatisch aus dem Kontext ab. Wenn Sie Handler in separate Funktionen extrahieren, verwenden Sie explizite Typen wie React.ChangeEvent für Input-Änderungen oder React.MouseEvent für Klicks. Der generische Parameter spezifiziert den Element-Typ, wie HTMLInputElement oder HTMLButtonElement.
ReactNode akzeptiert alles Renderbare, einschließlich Strings, Zahlen, null, undefined und JSX-Elemente. ReactElement akzeptiert nur JSX-Elemente und schließt Primitive aus. Verwenden Sie ReactNode für die meisten Fälle. Verwenden Sie ReactElement, wenn Ihre Komponente speziell JSX-Children erfordert und reinen Text oder Zahlen ablehnen soll.
Nein. Props, die von Server- zu Client-Komponenten übergeben werden, müssen serialisierbar sein. Funktionen, Klassen, Symbole und andere nicht-serialisierbare Werte können die Server-Client-Grenze nicht überschreiten. Definieren Sie Event-Handler und Callbacks innerhalb von Client-Komponenten oder verwenden Sie Server Actions für Formularübermittlungen, die serverseitige Verarbeitung benötigen.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.