From d6be64ce566d7db84e8a2b086d71e4d9a9850ff4 Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Sat, 22 Aug 2020 15:40:36 +0300 Subject: [PATCH] Add 'TypedQueryDocumentNode' TS type (#2749) --- integrationTests/ts/index.ts | 30 +++++++++++++++++++++-- src/index.d.ts | 1 + src/utilities/index.d.ts | 3 +++ src/utilities/typedQueryDocumentNode.d.ts | 14 +++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/utilities/typedQueryDocumentNode.d.ts diff --git a/integrationTests/ts/index.ts b/integrationTests/ts/index.ts index eb989414b3..51dea32f8f 100644 --- a/integrationTests/ts/index.ts +++ b/integrationTests/ts/index.ts @@ -1,6 +1,7 @@ +import { parse } from 'graphql/language'; import { GraphQLString, GraphQLSchema, GraphQLObjectType } from 'graphql/type'; -import { ExecutionResult } from 'graphql/execution'; -import { graphqlSync } from 'graphql'; +import { ExecutionResult, execute } from 'graphql/execution'; +import { TypedQueryDocumentNode, graphqlSync } from 'graphql'; interface SomeExtension { number: number; @@ -69,3 +70,28 @@ const result: ExecutionResult = graphqlSync({ `, variableValues: { who: 'Dolly' }, }); + +// Tests for TS specific TypedQueryDocumentNode type +const queryDocument = parse(` + query helloWho($who: String){ + test(who: $who) + } +`); + +type ResponseData = { test: string }; +const typedQueryDocument = queryDocument as TypedQueryDocumentNode< + ResponseData, + {} +>; + +// Supports conversion to DocumentNode +execute({ schema, document: typedQueryDocument }); + +function wrappedExecute(document: TypedQueryDocumentNode) { + return execute({ schema, document }) as ExecutionResult; +} + +const { data } = wrappedExecute(typedQueryDocument); +if (data != null) { + const typedData: ResponseData = data; +} diff --git a/src/index.d.ts b/src/index.d.ts index 9ff5ee02a6..0776078b8b 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -461,4 +461,5 @@ export { BuildSchemaOptions, BreakingChange, DangerousChange, + TypedQueryDocumentNode, } from './utilities/index'; diff --git a/src/utilities/index.d.ts b/src/utilities/index.d.ts index 658a69ac0d..a8019f99d8 100644 --- a/src/utilities/index.d.ts +++ b/src/utilities/index.d.ts @@ -112,5 +112,8 @@ export { DangerousChange, } from './findBreakingChanges'; +// Wrapper type that contains DocumentNode and types that can be deduced from it. +export { TypedQueryDocumentNode } from './typedQueryDocumentNode'; + // @deprecated: Report all deprecated usage within a GraphQL document. export { findDeprecatedUsages } from './findDeprecatedUsages'; diff --git a/src/utilities/typedQueryDocumentNode.d.ts b/src/utilities/typedQueryDocumentNode.d.ts new file mode 100644 index 0000000000..ef86c2ec71 --- /dev/null +++ b/src/utilities/typedQueryDocumentNode.d.ts @@ -0,0 +1,14 @@ +import { DocumentNode, ExecutableDefinitionNode } from '../language/ast'; + +/** + * Wrapper type that contains DocumentNode and types that can be deduced from it. + */ +interface TypedQueryDocumentNode< + TResponseData = Record, + TRequestVariables = Record +> extends DocumentNode { + readonly definitions: ReadonlyArray; + // FIXME: remove once TS implements proper way to enforce nominal typing + readonly __enforceStructuralTypingOnResponseDataType?: TResponseData; + readonly __enforceStructuralTypingOnRequestVariablesType?: TRequestVariables; +}