Skip to content

Commit

Permalink
helper to derive fragment types
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Nov 21, 2023
1 parent dfe3119 commit c91cc46
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 2 deletions.
31 changes: 31 additions & 0 deletions src/__tests__/fragments.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { assertType, test } from 'vitest';
import { Introspection } from '../introspection';
import { Document } from '../parser';
import { FragmentType } from '../typed-document/fragments';
import { schema } from './introspection.test-d';

type Intro = Introspection<typeof schema>;
const any = {} as any;

test('creates a type for a given fragment', () => {
const unionQuery = `
fragment Fields on Todo {
id
text
complete
__typename
}
`;

type doc = Document<typeof unionQuery>;
type typedDoc = FragmentType<doc, Intro>;

const actual = any as typedDoc;

assertType<{
id: string | number;
text: string;
complete: boolean | null;
__typename: 'Todo';
}>(actual);
});
23 changes: 23 additions & 0 deletions src/typed-document/fragments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FragmentDefinitionNode, Kind } from '@0no-co/graphql.web';
import { Selection, ObjectLikeType, FragmentMap } from './index';
import { Introspection as IntrospectionType } from '../introspection';

export type FragmentType<
Document extends { kind: Kind.DOCUMENT; definitions: any[] },
Introspection extends IntrospectionType<any>,
Fragments extends Record<string, FragmentDefinitionNode> = FragmentMap<Document>
> = Document['definitions'][0] extends {
kind: Kind.FRAGMENT_DEFINITION;
typeCondition: { name: { value: infer TypeName } };
}
? TypeName extends keyof Introspection['types']
? Introspection['types'][TypeName] extends ObjectLikeType
? Selection<
Document['definitions'][0]['selectionSet']['selections'],
Introspection['types'][TypeName],
Introspection,
Fragments
>
: never
: never
: never;
4 changes: 2 additions & 2 deletions src/typed-document/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type {
} from '../introspection';
import type { Obj, ObjValues } from '../utils';

type ObjectLikeType = {
export type ObjectLikeType = {
kind: 'OBJECT' | 'INTERFACE' | 'UNION';
name: string;
fields: { [key: string]: IntrospectionField };
Expand Down Expand Up @@ -119,7 +119,7 @@ type TypenameOfType<X extends ObjectLikeType> = X extends {
// i.e. excluding `PossibleFragmentsSelection<...>['__typename']` when we're on a GraphQL union or interface?
// TODO: For the interface case, do we need to type-union `FieldSelectionContinue<...>` into each possible
// intersection type of `PossibleFragmentsSelection<...>`?
type Selection<
export type Selection<
Selections extends readonly any[],
Type extends ObjectLikeType,
Introspection extends IntrospectionType<any>,
Expand Down

0 comments on commit c91cc46

Please sign in to comment.