Typy TypeScript pro vyhledávání/podmínky a unie
Mám jednoduchou otázku: Je možné získat typ části unie v TypeScriptu?
Například můžete často používat typy vyhledávání takto:
interface Person {
name: string;
}
type Name = Person['name']
Nyní předpokládám, že to není možné u svazků jako tento:
type Entity =
{ __type: 'Company', name: string }
| { __type: 'Employee', firstName: string };
Takže, existuje nějaký způsob, jak získat část svazu? Něco jako toto:
type Company = DoTheMagic<Entity, { __type: 'Employee' }>
const company: Company = ...;
console.log(company.name) // OK
console.log(company.firstName) // Compile error
Odpověď č. 1
Můžeme použít podmíněný typ Extract<T, U>. Pokud je T sjednocením, výsledkem Extract budou všichni členové sjednocení T, kteří splňují omezení U (tedy T extends U).
type Company = Extract<Entity, { __type: 'Employee' }>
// Same as
type Company = {
__type: "Employee";
firstName: string;
}
Odpověď č. 2
Správnou odpověď publikovala Titian Cernicova-Dragomir. Nicméně jsem se rozhodl zveřejnit úplné řešení mého širšího problému.
Pracujeme s ApolloClientem a často dostáváme unie. Kvůli silnému typování potřebujete spoustu ošklivých podmínek, abyste mohli přistupovat ke správným vlastnostem a udržet TypeScript spokojený.
Takže jsem přišel s následující funkcí asTypeGuard:
function asTypeGuard<Typename extends string>(typename: Typename) {
return function<Entity extends { __typename: string }>(
entity: Entity
): entity is Extract<Entity, { __typename: typeof typename }> {
return entity.__typename === typename;
};
}
const isCompany = asTypeGuard('Company');
const isEmployee = asTypeGuard('Employee');
let entity: { __typename: 'Company'; name: string } | { __typename: 'Employee'; firstName: string };
if (isCompany(entity)) {
console.log(entity.name);
console.log(entity.firstName) // Property 'firstName' does not exist on type '{ __typename: "Company"; name: string; }
} else if (isEmployee(entity)) {
console.log(entity.name); // Property 'name' does not exist on type '{ __typename: "Employee"; firstName: string; }
console.log(entity.firstName)
}
