Vyhledávání na webu

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)
}

Související články