TypeScript查找/条件类型和联合

时间:2018-07-11 12:22:07

标签: typescript union conditional-types

我有一个简单的问题:是否可以在TypeScript中获得联合的一部分的类型?

例如,您经常可以使用如下查询类型:

interface Person {
  name: string;
}

type Name = Person['name']

现在,我假设这样的联合是不可能的:

type Entity = 
    { __type: 'Company', name: string } 
  | { __type: 'Employee', firstName: string };

那么,有什么方法可以获取工会的一部分吗?像这样:

type Company = DoTheMagic<Entity, { __type: 'Employee' }> 

const company: Company = ...;

console.log(company.name) // OK
console.log(company.firstName) // Compile error

2 个答案:

答案 0 :(得分:2)

我们可以使用条件类型Extract<T, U>。如果T是联合,则Extract的结果将是T联合的所有成员,满足约束U(又名T extends U

type Company = Extract<Entity, { __type: 'Employee' }>  
// Same as
type Company = {
    __type: "Employee";
    firstName: string;
}

答案 1 :(得分:1)

正确答案由 Titian Cernicova-Dragomir发布。 但是,我决定为我的更广泛的问题发布完整的解决方案。

我们正在与ApolloClient合作,我们经常收到工会。由于要进行强类型输入,因此需要很多难看的条件才能访问正确的属性并保持TypeScript满意。

因此,我想到了以下 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)
}