Behalte an den Grenzen deiner Anwendung verlässliche Typen, dort wo DOM, Netzwerk und JSON dem Compiler entgleiten.
Diese Lektion in Kodokon öffnenEchter Code beginnt dort, wo perfekte Typen enden: DOM, Netzwerk und JSON liefern Daten, die TypeScript nicht erraten kann. Erste Grenze: das DOM. querySelector liefert Element | null zurück, einen Typ, der zu vage ist, um value zu lesen oder focus() aufzurufen.
const input = document.querySelector<HTMLInputElement>(
"#email"
);
if (input) {
input.value = "test@example.com";
input.focus();
}Der Typparameter ist ein Versprechen, das du dem Compiler gibst: Es liegt an dir, den richtigen Selektor zu treffen. Wenn das Element aus einem Event stammt, bevorzuge eine instanceof-Prüfung, die den Typ zur Laufzeit wirklich überprüft.
const form = document.querySelector("form");
form?.addEventListener("submit", (event) => {
event.preventDefault();
if (event.target instanceof HTMLFormElement) {
console.log(new FormData(event.target));
}
});Zweite Grenze: das Netzwerk. response.json() liefert Promise<any> zurück, also ein offenes Tor für alles. Das professionelle Minimum: den erwarteten Typ deklarieren, response.ok prüfen und den Aufruf in einer typisierten Funktion bündeln.
interface Post {
id: number;
title: string;
}
async function getPost(id: number): Promise<Post> {
const url = `https://api.example.com/posts/${id}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return (await response.json()) as Post;
}Dieses as Post bleibt ein reiner Vertrauensbeweis: Wenn sich die API ändert, warnt dich zur Laufzeit nichts. Schreibe für kritische Daten einen Type Guard, der die tatsächliche Form des JSON prüft, bevor du sie verwendest.
function isPost(value: unknown): value is Post {
if (typeof value !== "object" || value === null) {
return false;
}
const obj = value as Record<string, unknown>;
return (
typeof obj.id === "number" &&
typeof obj.title === "string"
);
}