Promises mit Typen
interface Nutzer { id: number; name: string }
function hole(id: number): Promise<Nutzer> {
return new Promise((resolve, reject) => {
setTimeout(() => (id > 0 ? resolve({ id, name: "Nutzer " + id }) : reject(new Error("ungültig"))), 10);
});
}
async function main(): Promise<void> {
const n = await hole(1); // n: Nutzer
console.log(n.name);
try {
await hole(-1);
} catch (e) {
console.log(e instanceof Error ? e.message : "unbekannt");
}
const alle = await Promise.all([hole(1), hole(2)]); // [Nutzer, Nutzer]
console.log(alle.map((x) => x.id));
}
main();Ausgabe
Nutzer 1 ungültig [ 1, 2 ]
Promise.all bewahrt die Typen eines Tupels:
async function demo() {
const [zahl, text, flag] = await Promise.all([Promise.resolve(1), Promise.resolve("a"), Promise.resolve(true)]);
console.log(zahl + 1, text.toUpperCase(), !flag);
}
demo();Ausgabe
2 A false
Fehler sind unknown
In einem catch hat die Variable den Typ unknown (mit strict). Prüfe sie, bevor du sie benutzt:
function fehlerText(e: unknown): string {
if (e instanceof Error) return e.message;
if (typeof e === "string") return e;
return "Unbekannter Fehler";
}
try {
JSON.parse("{kaputt");
} catch (e) {
console.log(fehlerText(e).length > 0, fehlerText("Text"), fehlerText(42));
}Ausgabe
true Text Unbekannter Fehler
Das große Problem: Daten von außen
TypeScript-Typen gibt es nur beim Entwickeln. Wenn Daten von außen kommen (fetch, JSON.parse, Formulare, Dateien, Umgebungsvariablen), weiß niemand, ob sie wirklich so aussehen:
interface Nutzer { id: number; name: string }
const roh = '{"id": "keine Zahl", "name": 5}';
const n = JSON.parse(roh) as Nutzer; // TypeScript glaubt dir blind
console.log(typeof n.id, n.name.toUpperCase); // id ist ein String! Läuft trotzdem bis zum AbsturzAusgabe
string undefined
JSON.parse liefert any, deshalb prüft nichts. Besser: zur Laufzeit validieren.
Von Hand: Type Guards
interface Nutzer { id: number; name: string }
function istNutzer(x: unknown): x is Nutzer {
return (
typeof x === "object" && x !== null &&
typeof (x as Record<string, unknown>).id === "number" &&
typeof (x as Record<string, unknown>).name === "string"
);
}
function parseNutzer(json: string): Nutzer {
const daten: unknown = JSON.parse(json);
if (!istNutzer(daten)) throw new TypeError("Kein gültiger Nutzer");
return daten;
}
console.log(parseNutzer('{"id": 1, "name": "Mia"}'));
try { parseNutzer('{"id": "x"}'); } catch (e) { console.log((e as Error).message); }Ausgabe
{ id: 1, name: 'Mia' }
Kein gültiger NutzerMit einer Bibliothek: Zod und Co.
Bei vielen Feldern wird das mühsam. Bibliotheken wie Zod, Valibot oder ArkType beschreiben die Form einmal und erzeugen daraus Prüfung und Typ:
import { z } from "zod";
const Nutzer = z.object({
id: z.number().int().positive(),
name: z.string().min(1),
email: z.string().email().optional(),
});
type Nutzer = z.infer<typeof Nutzer>; // Typ wird automatisch abgeleitet
const nutzer = Nutzer.parse(JSON.parse(roh)); // wirft einen verständlichen Fehler, wenn etwas nicht passtRegel: Alles, was die Anwendungsgrenze überschreitet, wird validiert. Danach kannst du dem Typ vertrauen.
fetch mit Typen
async function ladeNutzer(id: number): Promise<Nutzer> {
const antwort = await fetch(`/api/nutzer/${id}`);
if (!antwort.ok) throw new Error(`HTTP ${antwort.status}`);
const daten: unknown = await antwort.json();
return Nutzer.parse(daten); // Prüfung zur Laufzeit
}antwort.json() liefert Promise<any>. Gib das Ergebnis über unknown weiter und prüfe es.
Umgebungsvariablen
const env: Record<string, string | undefined> = { PORT: "8080" };
const port = Number(env.PORT ?? 3000);
if (Number.isNaN(port)) throw new Error("PORT ungültig");
console.log(port);Ausgabe
8080
Timeouts und Abbruch
function mitZeitlimit<T>(p: Promise<T>, ms: number): Promise<T> {
return Promise.race([
p,
new Promise<never>((_, reject) => setTimeout(() => reject(new Error("Zeitüberschreitung")), ms)),
]);
}
const langsam = new Promise<string>((r) => setTimeout(() => r("fertig"), 200));
mitZeitlimit(langsam, 20).catch((e: Error) => console.log(e.message));Ausgabe
Zeitüberschreitung
Merke
async-Funktionen liefernPromise<T>;Promise.allbehält Tupel-Typencatch (e):eistunknown, erst mitinstanceofprüfen- Typen existieren nur zur Entwicklungszeit: Daten von außen zur Laufzeit validieren
JSON.parseundresponse.json()liefernany: überunknownund Type Guards oder Zod prüfen- Mit
aszu behaupten ersetzt keine Prüfung
Aufgabe
Schreibe einen Type Guard für { titel: string; seiten: number } und eine Funktion, die ein JSON-Array solcher Bücher einliest und nur gültige zurückgibt.