Ссылка на сложный тип из объединения в 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.

Может утилитка типа?

1 ответ

Решение

Этот тип:

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"

Детская площадка

Таким образом, вы можете использовать пересечение, чтобы объединение разрешалось только к типу, который может соответствовать пересеченному типу.

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

Площадка с рабочим кодом


С помощью некоторого рефакторинга вы даже можете сделать это весьма неплохо с помощью красивого псевдонима универсального типа:

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

Детская площадка

Другие вопросы по тегам