在Typescript中从联合类型引用复杂类型

4

我正在使用graphql-codegen从我的GraphQL查询中生成类型。

结果有时会变得相当复杂,特别是涉及到unions的时候。

这里有一个具体的例子:

export type GroupQuery = { __typename?: 'Query' } & {
  group?: Maybe<
    { __typename?: 'Group' } & Pick<
      Group,
      'id' | 'name' 
    > & {
        criterions: Array<
          { __typename?: 'kindA' } & Pick<SomeModel, 'id' | 'type'> | 
          { __typename?: 'kindB' } & Pick<SomeOtherModel, 'id' | 'type' | 'count'>
        >
    }
  }

我试图通过__typename来引用联合类型的特定情况。
let kindB: NonNullable<GroupQuery['group']>['criterions'][0]// not sure where to go from here.

也许是一个实用类型?

1个回答

4
这种类型:
type T = NonNullable<GroupQuery['group']>['criterions'][0]`

将解决为以下类型:

type T = {
    __typename?: "kindA" | undefined;
    id: number;
    name: string;
} | {
    __typename?: "kindB" | undefined;
    id: number;
    name: string;
}

所以你真正要问的是如何获取联合中的分支:
__typename === 'kindB'

在这种情况下,您可以使用一个交集 & 来过滤一个联合类型。一般而言,它的用法如下:
type T = ("A" | "B" | "C") & "A" // "A"

Playground

因此,您可以使用交集使联合类型解析为只能匹配相交类型的类型。

type KindB =
    NonNullable<GroupQuery['group']>['criterions'][0] & { __typename: 'kindB' }

现在,KindB 解析为以下类型:
type KindB = {
    __typename?: "kindB" | undefined;
    id: number;
    name: string;
} & {
    __typename: 'kindB';
}

正如您所看到的,联合类型的成员kindA已经不存在了,而联合类型中剩余的成员正在与{ __typename: 'kindB' } 进行交集。如果应用这个交集,它将被简化为:
type KindB = {
    __typename: "kindB";
    id: number;
    name: string;
}

通过一些重构,你甚至可以使用一个不错的通用类型别名来实现:


带有可工作代码的 Playground

// Union of all criterion types
type GroupQueryCriterions =
    NonNullable<GroupQuery['group']>['criterions'][number]

// Get the branch of the criterions union that has a specific typename.
type GroupQueryCriterionType<T extends GroupQueryCriterions['__typename']> =
    GroupQueryCriterions & { __typename: T }

// Get a specific criterion type.
type KindB = GroupQueryCriterionType<'kindB'>

Playground

这是 TypeScript 的 Playground。

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