/
index.ts
145 lines (121 loc) · 3.7 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { parse } from 'graphql/language';
import { GraphQLString, GraphQLSchema, GraphQLObjectType } from 'graphql/type';
import { ExecutionResult, execute } from 'graphql/execution';
import { TypedQueryDocumentNode, graphqlSync } from 'graphql';
interface SomeExtension {
number: number;
string: string;
}
const example: SomeExtension = {
number: 42,
string: 'Meaning of life',
};
declare module 'graphql' {
interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> {
someObjectExtension?: SomeExtension;
}
interface GraphQLFieldExtensions<
_TSource,
_TContext,
_TArgs = { [argName: string]: any },
> {
someFieldExtension?: SomeExtension;
}
interface GraphQLArgumentExtensions {
someArgumentExtension?: SomeExtension;
}
}
const queryType: GraphQLObjectType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
sayHi: {
type: GraphQLString,
args: {
who: {
type: GraphQLString,
extensions: {
someArgumentExtension: example,
},
},
},
resolve: (_root, args) => 'Hello ' + (args.who || 'World'),
extensions: {
someFieldExtension: example,
},
},
}),
extensions: {
someObjectExtension: example,
},
});
const schema: GraphQLSchema = new GraphQLSchema({
query: queryType,
});
function checkExtensionTypes(_test: SomeExtension | null | undefined) {}
checkExtensionTypes(queryType?.extensions?.someObjectExtension);
const sayHiField = queryType?.getFields()?.sayHi;
checkExtensionTypes(sayHiField?.extensions?.someFieldExtension);
checkExtensionTypes(sayHiField?.args?.[0]?.extensions?.someArgumentExtension);
const result: ExecutionResult = graphqlSync({
schema,
source: `
query helloWho($who: String){
test(who: $who)
}
`,
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<T>(document: TypedQueryDocumentNode<T>) {
return execute({ schema, document }) as ExecutionResult<T>;
}
const { data } = wrappedExecute(typedQueryDocument);
if (data != null) {
const typedData: ResponseData = data;
}
declare function runQueryA(
q: TypedQueryDocumentNode<{ output: string }, { input: string | null }>,
): void;
// valid
declare const optionalInputRequiredOutput: TypedQueryDocumentNode<
{ output: string },
{ input: string | null }
>;
runQueryA(optionalInputRequiredOutput);
declare function runQueryB(
q: TypedQueryDocumentNode<{ output: string | null }, { input: string }>,
): void;
// still valid: We still accept {output: string} as a valid result.
// We're now passing in {input: string} which is still assignable to {input: string | null}
runQueryB(optionalInputRequiredOutput);
// valid: we now accept {output: null} as a valid Result
declare const optionalInputOptionalOutput: TypedQueryDocumentNode<
{ output: string | null },
{ input: string | null }
>;
runQueryB(optionalInputOptionalOutput);
// valid: we now only pass {input: string} to the query
declare const requiredInputRequiredOutput: TypedQueryDocumentNode<
{ output: string },
{ input: string }
>;
runQueryB(requiredInputRequiredOutput);
// valid: we now accept {output: null} as a valid Result AND
// we now only pass {input: string} to the query
declare const requiredInputOptionalOutput: TypedQueryDocumentNode<
{ output: null },
{ input: string }
>;
runQueryB(requiredInputOptionalOutput);