TypeScript查找/条件类型和联合类型

3

我有一个简单的问题:在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个回答

9
我们可以使用条件类型Extract<T, U>。如果T是一个联合类型,那么Extract的结果将会是所有满足约束U(即T extends U)的T联合类型成员。
type Company = Extract<Entity, { __type: 'Employee' }>  
// Same as
type Company = {
    __type: "Employee";
    firstName: string;
}

@Deftomat:)) 我会尽力的。 - Titian Cernicova-Dragomir

2

正确答案由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)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接