TypeScript, so viel du brauchst
Die Schreibweisen, die dir in React-Code ständig begegnen
TypeScript ist JavaScript, bei dem du dazuschreibst, welche Form deine Daten haben. Der Editor meckert dann, bevor der Browser es tut.
Du kannst JavaScript – dann fehlt dir hier erstaunlich wenig. Eine Handvoll Schreibweisen deckt fast alles ab, was in React-Projekten vorkommt.
Typen sind kein zweites Programm. Sie sind Kommentare, die überprüft werden. Vor dem Ausführen fliegen sie raus – im Browser läuft ganz normales JavaScript.
Die Grundform: Wert : Typ
let name: string = "Ada";
let alter: number = 36;
let aktiv: boolean = true;
let tags: string[] = ["react", "ts"]; // Array aus Strings
// Meistens brauchst du das gar nicht – TypeScript errät es selbst:
let titel = "Hallo"; // ist automatisch string
// titel = 42; // Fehler: eine Zahl passt hier nicht
// Funktionen: was rein geht, was raus kommt
function verdopple(n: number): number {
return n * 2;
}
const kuerze = (text: string, max = 20): string =>
text.length > max ? text.slice(0, max) + "…" : text; Schreib Typen nur an die Grenzen: Props, Funktionsparameter, Daten von einer API. Innendrin errät TypeScript sie selbst (InferenzTypeScript errät den Typ selbst, ohne dass du ihn hinschreibst.→). const x: number = 5 ist unnötiger Lärm.
Objekte beschreiben
type User = {
id: string;
name: string;
email?: string; // das ? heißt: darf fehlen
};
// Zwei Typen zusammenlegen:
type Admin = User & { rechte: string[] };
// interface kann fast dasselbe:
interface Product {
id: string;
price: number;
} `type` oder `interface`? Für React-Props fast egal. Faustregel: nimm type – es kann alles, was interface kann, und noch mehr.
Union: „eines von diesen“
Eine Union„Eines von diesen“: string | number heißt, der Wert ist entweder eine Zeichenkette oder eine Zahl.→ listet die erlaubten Möglichkeiten auf. Mit festen Wörtern wird daraus eine Auswahl, die dein Editor automatisch vervollständigt.
type Variante = "primary" | "ghost" | "danger";
type Id = string | number;
let v: Variante = "ghost"; // ok
// let w: Variante = "blau"; // Fehler – gibt es nicht Statt drei Wahrheitswerten (laedt, hatFehler, fertig), die sich widersprechen können, beschreibst du die Zustände einzeln. Dann ist ein unmöglicher Zustand gar nicht mehr hinschreibbar.
type Zustand =
| { status: "laedt" }
| { status: "fehler"; text: string }
| { status: "fertig"; daten: string[] };
function anzeigen(z: Zustand) {
if (z.status === "laedt") return "Lädt …";
if (z.status === "fehler") return z.text; // text gibt es hier sicher
return z.daten.length + " Treffer"; // daten gibt es hier sicher
} Fachbegriff: Discriminated UnionMehrere Varianten mit einem gemeinsamen Erkennungsfeld – daran sieht TypeScript, welche Variante gerade vorliegt.Beispiel: { status: "laden" } oder { status: "fehler", text: string }. Fragst du status ab, weiß TypeScript, welche Felder es gibt.→. Und das Eingrenzen durch die Abfrage heißt NarrowingTypeScript grenzt einen Typ ein, wenn du ihn abfragst: nach if (typeof x === "number") weiß es, dass x eine Zahl ist.→. Die Namen musst du nicht behalten – das Muster schon.
Fertige Helfer, die du oft siehst
type User = { id: string; name: string; email: string };
Partial<User> // alle Felder optional -> für Updates
Pick<User, "id" | "name"> // nur diese beiden Felder
Omit<User, "email"> // alles außer email -> sehr häufig
Record<string, number> // Objekt: Text-Schlüssel, Zahl-Wert
keyof User // "id" | "name" | "email" ▸ Genauer erklärt: Generics, as const, satisfies und unknown optional
Generics – ein Platzhalter für einen Typ
Ein GenericEin Platzhalter für einen Typ, der erst beim Benutzen festgelegt wird – das <T> in Array<T>.→ ist eine Variable für Typen. Du kennst das schon aus useState<number>(0). Selbst schreiben musst du sie selten, lesen können solltest du sie.
// T wird erst beim Aufruf festgelegt
function erstes<T>(items: T[]): T | undefined {
return items[0];
}
erstes([1, 2, 3]); // T = number
erstes(["a", "b"]); // T = string
// Mit Bedingung: T muss ein id-Feld haben
function nachId<T extends { id: string }>(items: T[], id: string) {
return items.find((i) => i.id === id);
} as const – Werte einfrieren
const ROLLEN = ["admin", "user"];
// Typ: string[]
type Rolle = (typeof ROLLEN)[number];
// -> string (nutzlos) const ROLLEN = ["admin", "user"] as const;
// Typ: readonly ["admin", "user"]
type Rolle = (typeof ROLLEN)[number];
// -> "admin" | "user" ✨ So leitest du Typen aus deinen Daten ab, statt beides doppelt zu pflegen.
satisfies – prüfen, ohne zu vergröbern
type Theme = Record<string, string>;
const theme = {
bg: "#0a0c11",
ink: "#e8ebf4",
} satisfies Theme;
theme.bg; // der Editor kennt bg und ink
// theme.foo; // Fehler – gibt es nicht
// Mit ": Theme" statt "satisfies Theme" wäre theme.foo durchgegangen. unknown statt any
any schaltet die Prüfung ab – überall dort, wo der Wert danach noch hinfließt. unknown sagt „ich weiß es noch nicht“ und zwingt dich, vorher nachzusehen.
try {
riskant();
} catch (err) { // err ist unknown, nicht Error!
const text = err instanceof Error ? err.message : "Unbekannter Fehler";
console.error(text);
} TypeScript prüft vorher und wird dann herausgeworfen. Daten von einer API sind also nicht automatisch korrekt, nur weil du sie User genannt hast. Wer das absichern will, prüft zur Laufzeit – zum Beispiel mit Zod.
Sitzt das schon?
5 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.
Jetzt selbst schreiben
Die Komponente zeigt einen Ladezustand an – aktuell mit any verkabelt und ohne Ausgabe. Sorge dafür, dass jeder Zustand die richtige Anzeige erzeugt.
- □Bei
laedterscheintLädt … - □Bei
fehlererscheint der Text austext - □Bei
fertigerscheintN Treffer(N = Länge vondaten) - □Bonus: ersetze
anydurch eine Union aus drei Varianten
type Zustand = any; // TODO: durch eine Union aus drei Varianten ersetzen export function Anzeige({ z }: { z: Zustand }) { // TODO: pro Zustand die richtige Ausgabe zurückgeben return <p>???</p>; } export default function App() { return ( <div style={{ fontFamily: "system-ui", padding: 16 }}> <Anzeige z={{ status: "laedt" }} /> <Anzeige z={{ status: "fehler", text: "Server weg" }} /> <Anzeige z={{ status: "fertig", daten: ["a", "b", "c"] }} /> </div> ); }