Du beherrschst die Mechanismen der Typverengung und schreibst eigene Prädikate, um Unions verlässlicher zu machen.
Diese Lektion in Kodokon öffnenNarrowing ist der Mechanismus, mit dem TypeScript einen weiten Typ (eine Union, unknown) durch die Analyse des Kontrollflusses zu einem präziseren verfeinert. Du nutzt ihn bereits, ohne darüber nachzudenken: Jedes if auf einem typisierten Wert löst eine Analyse aus. Der Compiler kennt drei Familien eingebauter Guards: typeof für primitive Werte, instanceof für Klasseninstanzen und in für das Vorhandensein einer Eigenschaft. Wählst du sie gut, ersparst du dir leichtsinnige Casts mit as, die die Prüfung abschalten, statt sie zu lenken.
function formatValue(
value: string | number | Date
): string {
if (typeof value === "string") {
return value.trim();
}
if (value instanceof Date) {
return value.toISOString();
}
return value.toFixed(2);
}Der in-Operator ist unbezahlbar, wenn du strukturell unterschiedliche Objekte verarbeitest, die kein Unterscheidungsfeld besitzen. Aber Vorsicht: in prüft das Vorhandensein eines Schlüssels, auch eines geerbten, nicht dessen Typ. Bei externen Daten (APIs, JSON) verengt er gemäß deinen Deklarationen, nicht gemäß dem, was zur Laufzeit tatsächlich existiert. Die klassische Falle aus der Praxis: Eine Prüfung typeof value === "object" lässt null durch, denn typeof null ist in JavaScript "object". Ergänze immer eine explizite Null-Prüfung.
interface Cat { meow(): void }
interface Dog { bark(): void }
function speak(animal: Cat | Dog): void {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}Wenn die Guard-Logik wiederverwendbar oder für die Flussanalyse zu komplex wird, schreibst du einen eigenen Type Guard: eine Funktion, deren Rückgabetyp mit value is T annotiert ist. Der Compiler vertraut dann deiner Implementierung. Das ist ein Vertrag: Ist deine Prüfung unvollständig, führst du Laufzeitfehler wieder ein, die das Typsystem nie zu sehen bekommt. Seit TypeScript 5.5 werden einfache Prädikate in Callbacks wie filter automatisch abgeleitet, doch für exportierte Funktionen bleibt die explizite Annotation die Norm.
interface User { id: string; email: string }
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
typeof value.id === "string" &&
"email" in value &&
typeof value.email === "string"
);
}
const raw: unknown =
JSON.parse('{"id":"1","email":"a@b.c"}');
if (isUser(raw)) {
console.log(raw.email.toLowerCase());
}typeof value === "object" nicht, um auf ein Objekt ungleich null zu verengen?booleanvalue as Uservalue is Userasserts value