Reputation: 1964
I'm using graphql-codegen
to generate types from my graphQL queries.
The result is sometimes pretty complex specifically when unions
are involved.
Here is a concrete example
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'>
>
}
}
So I'm trying to do is be able to refer to a specific case of the union based on the __typename
let kindB: NonNullable<GroupQuery['group']>['criterions'][0]// not sure where to go from here.
Maybe a utility type?
Upvotes: 4
Views: 1389
Reputation: 187014
This type:
type T = NonNullable<GroupQuery['group']>['criterions'][0]`
Would be resolved to this type:
type T = {
__typename?: "kindA" | undefined;
id: number;
name: string;
} | {
__typename?: "kindB" | undefined;
id: number;
name: string;
}
So what you are really asking how to get the branch of the union where:
__typename === 'kindB'
In that case you can use an intersection &
to filter a union type. In general that works like this:
type T = ("A" | "B" | "C") & "A" // "A"
So you can use an intersection to make the union resolve to only the type that could match the intersected type.
type KindB =
NonNullable<GroupQuery['group']>['criterions'][0] & { __typename: 'kindB' }
Now KindB
resolves to this type:
type KindB = {
__typename?: "kindB" | undefined;
id: number;
name: string;
} & {
__typename: 'kindB';
}
As you can see, the kindA
member of the union is no longer present, and the remaining member of the union is being intersected with { __typename: 'kindB' }
. If you apply that intersection, it reduces to:
type KindB = {
__typename: "kindB";
id: number;
name: string;
}
With some refactoring, you could even make this quite nice with a nice generic type alias:
// 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'>
Upvotes: 4