From 666298632327d34b23ed7b1f5ecf1f25cdb5c068 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Wed, 10 Jan 2024 20:02:44 +0000 Subject: [PATCH] Abstract annotated document nodes into TadaDocumentNode --- src/__tests__/selection.test-d.ts | 4 +-- src/api.ts | 46 ++++++++++++++++++++++++++----- src/namespace.ts | 31 +++++++++++++-------- src/utils.ts | 25 ----------------- 4 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/__tests__/selection.test-d.ts b/src/__tests__/selection.test-d.ts index 6dd6c045..e320474c 100644 --- a/src/__tests__/selection.test-d.ts +++ b/src/__tests__/selection.test-d.ts @@ -1,6 +1,6 @@ import { expectTypeOf, test } from 'vitest'; import { simpleSchema } from './fixtures/simpleSchema'; -import { $tada, decorateDocument, getFragmentsOfDocumentsRec } from '../namespace'; +import { $tada, decorateFragmentDef, getFragmentsOfDocumentsRec } from '../namespace'; import { parseDocument } from '../parser'; import { mapIntrospection } from '../introspection'; import { getDocumentType } from '../selection'; @@ -121,7 +121,7 @@ test('infers fragment spreads for fragment refs', () => { query { ...Fields } `>; - type extraFragments = getFragmentsOfDocumentsRec<[decorateDocument]>; + type extraFragments = getFragmentsOfDocumentsRec<[decorateFragmentDef]>; type actual = getDocumentType; type expected = { diff --git a/src/api.ts b/src/api.ts index 0b5793b0..f153934a 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,4 +1,5 @@ import { parse as _parse } from '@0no-co/graphql.web'; +import type { DocumentNode } from '@0no-co/graphql.web'; import type { IntrospectionQuery, @@ -8,15 +9,16 @@ import type { } from './introspection'; import type { - decorateDocument, + decorateFragmentDef, getFragmentsOfDocumentsRec, - FragmentDocumentNode, + FragmentDefDecorationLike, + FragmentDefDecoration, } from './namespace'; import type { getDocumentType } from './selection'; import type { getVariablesType } from './variables'; import type { parseDocument, DocumentNodeLike } from './parser'; -import type { stringLiteral, TypedDocumentNode } from './utils'; +import type { stringLiteral, DocumentDecoration } from './utils'; interface AbstractSetupSchema { introspection: IntrospectionQuery; @@ -44,14 +46,17 @@ type getDocumentNode< ? getDocumentType extends infer Result ? Result extends never ? never - : TypedDocumentNode> & - decorateDocument + : TadaDocumentNode< + Result, + getVariablesType, + decorateFragmentDef + > : never : never; function graphql< const In extends stringLiteral, - const Fragments extends readonly [...FragmentDocumentNode[]], + const Fragments extends readonly [...FragmentDefDecorationLike[]], >( input: In, _fragments?: Fragments @@ -59,5 +64,32 @@ function graphql< return _parse(input) as any; } +/** A GraphQL `DocumentNode` with attached generics for its result data and variables. + * + * @remarks + * A GraphQL {@link DocumentNode} defines both the variables it accepts on request and the `data` + * shape it delivers on a response in the GraphQL query language. + * + * To bridge the gap to TypeScript, tools may be used to generate TypeScript types that define the shape + * of `data` and `variables` ahead of time. These types are then attached to GraphQL documents using this + * `TypedDocumentNode` type. + * + * Using a `DocumentNode` that is typed like this will cause any `urql` API to type its input `variables` + * and resulting `data` using the types provided. + * + * @privateRemarks + * For compatibility reasons this type has been copied and internalized from: + * https://github.com/dotansimha/graphql-typed-document-node/blob/3711b12/packages/core/src/index.ts#L3-L10 + * + * @see {@link https://github.com/dotansimha/graphql-typed-document-node} for more information. + */ +interface TadaDocumentNode< + Result = { [key: string]: any }, + Variables = { [key: string]: any }, + Decoration = never, +> extends DocumentNode, + DocumentDecoration, + FragmentDefDecoration {} + export { parse, graphql }; -export type { setupSchema }; +export type { setupSchema, TadaDocumentNode }; diff --git a/src/namespace.ts b/src/namespace.ts index 3e841164..a1bfab27 100644 --- a/src/namespace.ts +++ b/src/namespace.ts @@ -1,5 +1,6 @@ import type { Kind } from '@0no-co/graphql.web'; import type { DocumentNodeLike } from './parser'; +import type { obj } from './utils'; /** Private namespace holding our symbols for markers. * @@ -20,23 +21,19 @@ declare namespace $tada { export type fragmentId = typeof fragmentId; } -type decorateDocument = Document['definitions'][0] extends { +type decorateFragmentDef = Document['definitions'][0] extends { kind: Kind.FRAGMENT_DEFINITION; name: any; typeCondition: any; } - ? { - [$tada.fragmentDef]?: Document['definitions'][0] & { - readonly [$tada.fragmentId]: unique symbol; - }; - } - : {}; + ? obj + : never; type getFragmentsOfDocumentsRec = Documents extends readonly [ infer Document, ...infer Rest, ] - ? (Document extends { [$tada.fragmentDef]?: any } + ? (Document extends FragmentDefDecorationLike ? Exclude extends infer FragmentDef extends { kind: Kind.FRAGMENT_DEFINITION; name: any; @@ -48,13 +45,25 @@ type getFragmentsOfDocumentsRec = Documents extends readonly [ getFragmentsOfDocumentsRec : {}; -type FragmentDocumentNode = { +interface FragmentDefDecorationLike { [$tada.fragmentDef]?: { readonly [$tada.fragmentId]: symbol; kind: Kind.FRAGMENT_DEFINITION; name: any; typeCondition: any; }; -}; +} -export type { $tada, decorateDocument, getFragmentsOfDocumentsRec, FragmentDocumentNode }; +interface FragmentDefDecoration { + [$tada.fragmentDef]?: Definition extends FragmentDefDecorationLike[$tada.fragmentDef] + ? Definition + : never; +} + +export type { + $tada, + decorateFragmentDef, + getFragmentsOfDocumentsRec, + FragmentDefDecorationLike, + FragmentDefDecoration, +}; diff --git a/src/utils.ts b/src/utils.ts index 034384e8..6b30158f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -54,28 +54,3 @@ export interface DocumentDecoration< */ __ensureTypesOfVariablesAndResultMatching?: (variables: Variables) => Result; } - -/** A GraphQL `DocumentNode` with attached generics for its result data and variables. - * - * @remarks - * A GraphQL {@link DocumentNode} defines both the variables it accepts on request and the `data` - * shape it delivers on a response in the GraphQL query language. - * - * To bridge the gap to TypeScript, tools may be used to generate TypeScript types that define the shape - * of `data` and `variables` ahead of time. These types are then attached to GraphQL documents using this - * `TypedDocumentNode` type. - * - * Using a `DocumentNode` that is typed like this will cause any `urql` API to type its input `variables` - * and resulting `data` using the types provided. - * - * @privateRemarks - * For compatibility reasons this type has been copied and internalized from: - * https://github.com/dotansimha/graphql-typed-document-node/blob/3711b12/packages/core/src/index.ts#L3-L10 - * - * @see {@link https://github.com/dotansimha/graphql-typed-document-node} for more information. - */ -export interface TypedDocumentNode< - Result = { [key: string]: any }, - Variables = { [key: string]: any }, -> extends DocumentNode, - DocumentDecoration {}