typeguards - Proč TypeScript type guard nezúžuje unii?
V následujícím TypeScript kódu očekávám, že contact bude zúžen na GroupContact díky použití typu strážce, ale contact je stále Contact | GroupContact. Proč?
type Contact = {
id: string;
name: string;
contactInfo: {
isStarred: boolean;
}
}
type GroupContact = Omit<Contact, "contactInfo">;
function isGroupContact(contact: Contact | GroupContact): contact is GroupContact {
return !("contactInfo" in contact);
}
function foo(contact: Contact | GroupContact) {
if (isGroupContact(contact)) {
console.log("group", contact); // why contact is `Contact | GroupContact` here and not `GroupContact`?
} else {
console.log("not group", contact);
}
}
Odpověď č. 1
Protože TypeScript používá strukturální typování. Zvažte tento příklad z příručky:
let o = { x: "hi", extra: 1 }; // ok let o2: { x: string } = o; // okZde má objektový literál
{ x: "hi", extra: 1 }odpovídající literální typ{ x: string, extra: number }. Tento typ je přiřaditelný k{ x: string }, protože má všechny požadované vlastnosti a tyto vlastnosti mají přiřaditelné typy. Přebytečná vlastnost při přiřazení nebrání, jen z něj činí podtyp{ x: string }.
Kompilátor pracuje se kódem, který jste ukázal, stejným způsobem jako v příkladu: odvozený typ GroupContact vypadá po rozšíření takto:
TS hřiště
type GroupContact = Omit<Contact, "contactInfo">;
/* ^?
type GroupContact = {
id: string;
name: string;
}
*/
Typ GroupContact neomezuje přítomnost vlastnosti contactInfo — znamená to pouze, že typ musí mít vlastnosti id a name typu string — což znamená, že jakákoli hodnota typu Contact je také přiřaditelná k GroupContact, takže typová ochrana ve skutečnosti nezúží typ.
Chcete-li naznačit, že typ „nesmí mít definovanou hodnotu u vlastnosti contactInfo“, můžete použít volitelnou vlastnost typu never:
type GroupContact = Omit<Contact, "contactInfo"> & { contactInfo?: never };
…a zúžení bude fungovat podle očekávání:
function foo(contact: Contact | GroupContact) {
if (isGroupContact(contact)) {
console.log("group", contact);
// ^? (parameter) contact: GroupContact
} else {
console.log("not group", contact);
// ^? (parameter) contact: Contact
}
}
Kód v TS Playground
Viz také: exactOptionalPropertyTypes
