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

Reference Complex type from union in Typescript

我正在使用 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.

也许是实用类型?

这种类型:

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 with working code


通过一些重构,您甚至可以使用一个很好的泛型类型别名使它变得非常漂亮:

// 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