Skip to content

Commit dcea440

Browse files
committed
introduce new Intersection type
Intersections of unions and interfaces can be considered to "implement" their unions and interface members. A type with a field of type Intersection will satisfy an interface where the field defined in the interface is one of the member types of the intersection. Alternative to #3527
1 parent 5ccf579 commit dcea440

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2455
-50
lines changed

docs-old/APIReference-GraphQL.md

+6
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ _Type Definitions_
6666
A union type within GraphQL that defines a list of implementations.
6767
</a>
6868
</li>
69+
<li>
70+
<a href="../type/#graphqlintersectiontype">
71+
<pre>class GraphQLIntersectionType</pre>
72+
An intersection type within GraphQL that defines a list of constraining types.
73+
</a>
74+
</li>
6975
<li>
7076
<a href="../type/#graphqlenumtype">
7177
<pre>class GraphQLEnumType</pre>

docs-old/APIReference-TypeSystem.md

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ _Definitions_
5454
A union type within GraphQL that defines a list of implementations.
5555
</a>
5656
</li>
57+
<li>
58+
<a href="#graphqlintersectiontype">
59+
<pre>class GraphQLIntersectionType</pre>
60+
An intersection type within GraphQL that defines a list of constraining types.
61+
</a>
62+
</li>
5763
<li>
5864
<a href="#graphqlenumtype">
5965
<pre>class GraphQLEnumType</pre>

src/__testUtils__/kitchenSinkSDL.ts

+12
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ extend union Feed = Photo | Video
7979
8080
extend union Feed @onUnion
8181
82+
intersection Resource = Feed & Node
83+
84+
intersection AnnotatedIntersection @onIntersection = Feed & Node
85+
86+
intersection AnnotatedIntersectionTwo @onIntersection = Feed & Node
87+
88+
intersection UndefinedIntersection
89+
90+
extend intersection Resource = Media & Accessible
91+
92+
extend intersection Resource @onIntersection
93+
8294
scalar CustomScalar
8395
8496
scalar AnnotatedScalar @onScalar

src/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export {
4444
GraphQLObjectType,
4545
GraphQLInterfaceType,
4646
GraphQLUnionType,
47+
GraphQLIntersectionType,
4748
GraphQLEnumType,
4849
GraphQLInputObjectType,
4950
GraphQLList,
@@ -90,6 +91,7 @@ export {
9091
isObjectType,
9192
isInterfaceType,
9293
isUnionType,
94+
isIntersectionType,
9395
isEnumType,
9496
isInputObjectType,
9597
isListType,
@@ -115,6 +117,7 @@ export {
115117
assertObjectType,
116118
assertInterfaceType,
117119
assertUnionType,
120+
assertIntersectionType,
118121
assertEnumType,
119122
assertInputObjectType,
120123
assertListType,
@@ -181,6 +184,8 @@ export type {
181184
GraphQLInputObjectTypeExtensions,
182185
GraphQLInterfaceTypeConfig,
183186
GraphQLInterfaceTypeExtensions,
187+
GraphQLIntersectionTypeConfig,
188+
GraphQLIntersectionTypeExtensions,
184189
GraphQLIsTypeOfFn,
185190
GraphQLObjectTypeConfig,
186191
GraphQLObjectTypeExtensions,

src/language/__tests__/predicates-test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('AST node predicates', () => {
3434
'ObjectTypeDefinition',
3535
'InterfaceTypeDefinition',
3636
'UnionTypeDefinition',
37+
'IntersectionTypeDefinition',
3738
'EnumTypeDefinition',
3839
'InputObjectTypeDefinition',
3940
'DirectiveDefinition',
@@ -42,6 +43,7 @@ describe('AST node predicates', () => {
4243
'ObjectTypeExtension',
4344
'InterfaceTypeExtension',
4445
'UnionTypeExtension',
46+
'IntersectionTypeExtension',
4547
'EnumTypeExtension',
4648
'InputObjectTypeExtension',
4749
]);
@@ -102,6 +104,7 @@ describe('AST node predicates', () => {
102104
'ObjectTypeDefinition',
103105
'InterfaceTypeDefinition',
104106
'UnionTypeDefinition',
107+
'IntersectionTypeDefinition',
105108
'EnumTypeDefinition',
106109
'InputObjectTypeDefinition',
107110
'DirectiveDefinition',
@@ -114,6 +117,7 @@ describe('AST node predicates', () => {
114117
'ObjectTypeDefinition',
115118
'InterfaceTypeDefinition',
116119
'UnionTypeDefinition',
120+
'IntersectionTypeDefinition',
117121
'EnumTypeDefinition',
118122
'InputObjectTypeDefinition',
119123
]);
@@ -126,6 +130,7 @@ describe('AST node predicates', () => {
126130
'ObjectTypeExtension',
127131
'InterfaceTypeExtension',
128132
'UnionTypeExtension',
133+
'IntersectionTypeExtension',
129134
'EnumTypeExtension',
130135
'InputObjectTypeExtension',
131136
]);
@@ -137,6 +142,7 @@ describe('AST node predicates', () => {
137142
'ObjectTypeExtension',
138143
'InterfaceTypeExtension',
139144
'UnionTypeExtension',
145+
'IntersectionTypeExtension',
140146
'EnumTypeExtension',
141147
'InputObjectTypeExtension',
142148
]);

src/language/__tests__/schema-parser-test.ts

+96
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ describe('Schema Parser', () => {
282282
locations: [{ line: 1, column: 19 }],
283283
});
284284

285+
expectSyntaxError('extend intersection Hello').to.deep.equal({
286+
message: 'Syntax Error: Unexpected <EOF>.',
287+
locations: [{ line: 1, column: 26 }],
288+
});
289+
285290
expectSyntaxError('extend enum Hello').to.deep.equal({
286291
message: 'Syntax Error: Unexpected <EOF>.',
287292
locations: [{ line: 1, column: 18 }],
@@ -961,6 +966,97 @@ describe('Schema Parser', () => {
961966
});
962967
});
963968

969+
it('Simple union', () => {
970+
const doc = parse('intersection Hello = World');
971+
972+
expectJSON(doc).toDeepEqual({
973+
kind: 'Document',
974+
definitions: [
975+
{
976+
kind: 'IntersectionTypeDefinition',
977+
name: nameNode('Hello', { start: 13, end: 18 }),
978+
description: undefined,
979+
directives: [],
980+
types: [typeNode('World', { start: 21, end: 26 })],
981+
loc: { start: 0, end: 26 },
982+
},
983+
],
984+
loc: { start: 0, end: 26 },
985+
});
986+
});
987+
988+
it('Intersection with two types', () => {
989+
const doc = parse('intersection Hello = Wo & Rld');
990+
991+
expectJSON(doc).toDeepEqual({
992+
kind: 'Document',
993+
definitions: [
994+
{
995+
kind: 'IntersectionTypeDefinition',
996+
name: nameNode('Hello', { start: 13, end: 18 }),
997+
description: undefined,
998+
directives: [],
999+
types: [
1000+
typeNode('Wo', { start: 21, end: 23 }),
1001+
typeNode('Rld', { start: 26, end: 29 }),
1002+
],
1003+
loc: { start: 0, end: 29 },
1004+
},
1005+
],
1006+
loc: { start: 0, end: 29 },
1007+
});
1008+
});
1009+
1010+
it('Intersection with two types and leading ampersand', () => {
1011+
const doc = parse('intersection Hello = & Wo & Rld');
1012+
1013+
expectJSON(doc).toDeepEqual({
1014+
kind: 'Document',
1015+
definitions: [
1016+
{
1017+
kind: 'IntersectionTypeDefinition',
1018+
name: nameNode('Hello', { start: 13, end: 18 }),
1019+
description: undefined,
1020+
directives: [],
1021+
types: [
1022+
typeNode('Wo', { start: 23, end: 25 }),
1023+
typeNode('Rld', { start: 28, end: 31 }),
1024+
],
1025+
loc: { start: 0, end: 31 },
1026+
},
1027+
],
1028+
loc: { start: 0, end: 31 },
1029+
});
1030+
});
1031+
1032+
it('Intersection fails with no types', () => {
1033+
expectSyntaxError('intersection Hello = &').to.deep.equal({
1034+
message: 'Syntax Error: Expected Name, found <EOF>.',
1035+
locations: [{ line: 1, column: 23 }],
1036+
});
1037+
});
1038+
1039+
it('Intersection fails with leading double ampersand', () => {
1040+
expectSyntaxError('intersection Hello = && Wo & Rld').to.deep.equal({
1041+
message: 'Syntax Error: Expected Name, found "&".',
1042+
locations: [{ line: 1, column: 23 }],
1043+
});
1044+
});
1045+
1046+
it('Intersection fails with double ampersand', () => {
1047+
expectSyntaxError('intersection Hello = Wo && Rld').to.deep.equal({
1048+
message: 'Syntax Error: Expected Name, found "&".',
1049+
locations: [{ line: 1, column: 26 }],
1050+
});
1051+
});
1052+
1053+
it('Intersection fails with trailing ampersand', () => {
1054+
expectSyntaxError('intersection Hello = & Wo & Rld &').to.deep.equal({
1055+
message: 'Syntax Error: Expected Name, found <EOF>.',
1056+
locations: [{ line: 1, column: 34 }],
1057+
});
1058+
});
1059+
9641060
it('Scalar', () => {
9651061
const doc = parse('scalar Hello');
9661062

src/language/__tests__/schema-printer-test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ describe('Printer: SDL document', () => {
110110
111111
extend union Feed @onUnion
112112
113+
intersection Resource = Feed & Node
114+
115+
intersection AnnotatedIntersection @onIntersection = Feed & Node
116+
117+
intersection AnnotatedIntersectionTwo @onIntersection = Feed & Node
118+
119+
intersection UndefinedIntersection
120+
121+
extend intersection Resource = Media & Accessible
122+
123+
extend intersection Resource @onIntersection
124+
113125
scalar CustomScalar
114126
115127
scalar AnnotatedScalar @onScalar

src/language/ast.ts

+23
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ export type ASTNode =
169169
| InputValueDefinitionNode
170170
| InterfaceTypeDefinitionNode
171171
| UnionTypeDefinitionNode
172+
| IntersectionTypeDefinitionNode
172173
| EnumTypeDefinitionNode
173174
| EnumValueDefinitionNode
174175
| InputObjectTypeDefinitionNode
@@ -178,6 +179,7 @@ export type ASTNode =
178179
| ObjectTypeExtensionNode
179180
| InterfaceTypeExtensionNode
180181
| UnionTypeExtensionNode
182+
| IntersectionTypeExtensionNode
181183
| EnumTypeExtensionNode
182184
| InputObjectTypeExtensionNode;
183185

@@ -263,6 +265,7 @@ export const QueryDocumentKeys: {
263265
'fields',
264266
],
265267
UnionTypeDefinition: ['description', 'name', 'directives', 'types'],
268+
IntersectionTypeDefinition: ['description', 'name', 'directives', 'types'],
266269
EnumTypeDefinition: ['description', 'name', 'directives', 'values'],
267270
EnumValueDefinition: ['description', 'name', 'directives'],
268271
InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'],
@@ -275,6 +278,7 @@ export const QueryDocumentKeys: {
275278
ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
276279
InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
277280
UnionTypeExtension: ['name', 'directives', 'types'],
281+
IntersectionTypeExtension: ['name', 'directives', 'types'],
278282
EnumTypeExtension: ['name', 'directives', 'values'],
279283
InputObjectTypeExtension: ['name', 'directives', 'fields'],
280284
};
@@ -568,6 +572,7 @@ export type TypeDefinitionNode =
568572
| ObjectTypeDefinitionNode
569573
| InterfaceTypeDefinitionNode
570574
| UnionTypeDefinitionNode
575+
| IntersectionTypeDefinitionNode
571576
| EnumTypeDefinitionNode
572577
| InputObjectTypeDefinitionNode;
573578

@@ -628,6 +633,15 @@ export interface UnionTypeDefinitionNode {
628633
readonly types?: ReadonlyArray<NamedTypeNode>;
629634
}
630635

636+
export interface IntersectionTypeDefinitionNode {
637+
readonly kind: Kind.INTERSECTION_TYPE_DEFINITION;
638+
readonly loc?: Location;
639+
readonly description?: StringValueNode;
640+
readonly name: NameNode;
641+
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
642+
readonly types?: ReadonlyArray<NamedTypeNode>;
643+
}
644+
631645
export interface EnumTypeDefinitionNode {
632646
readonly kind: Kind.ENUM_TYPE_DEFINITION;
633647
readonly loc?: Location;
@@ -684,6 +698,7 @@ export type TypeExtensionNode =
684698
| ObjectTypeExtensionNode
685699
| InterfaceTypeExtensionNode
686700
| UnionTypeExtensionNode
701+
| IntersectionTypeExtensionNode
687702
| EnumTypeExtensionNode
688703
| InputObjectTypeExtensionNode;
689704

@@ -720,6 +735,14 @@ export interface UnionTypeExtensionNode {
720735
readonly types?: ReadonlyArray<NamedTypeNode>;
721736
}
722737

738+
export interface IntersectionTypeExtensionNode {
739+
readonly kind: Kind.INTERSECTION_TYPE_EXTENSION;
740+
readonly loc?: Location;
741+
readonly name: NameNode;
742+
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
743+
readonly types?: ReadonlyArray<NamedTypeNode>;
744+
}
745+
723746
export interface EnumTypeExtensionNode {
724747
readonly kind: Kind.ENUM_TYPE_EXTENSION;
725748
readonly loc?: Location;

src/language/directiveLocation.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export enum DirectiveLocation {
1919
ARGUMENT_DEFINITION = 'ARGUMENT_DEFINITION',
2020
INTERFACE = 'INTERFACE',
2121
UNION = 'UNION',
22+
INTERSECTION = 'INTERSECTION',
2223
ENUM = 'ENUM',
2324
ENUM_VALUE = 'ENUM_VALUE',
2425
INPUT_OBJECT = 'INPUT_OBJECT',

src/language/kinds.ts

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export enum Kind {
4949
INPUT_VALUE_DEFINITION = 'InputValueDefinition',
5050
INTERFACE_TYPE_DEFINITION = 'InterfaceTypeDefinition',
5151
UNION_TYPE_DEFINITION = 'UnionTypeDefinition',
52+
INTERSECTION_TYPE_DEFINITION = 'IntersectionTypeDefinition',
5253
ENUM_TYPE_DEFINITION = 'EnumTypeDefinition',
5354
ENUM_VALUE_DEFINITION = 'EnumValueDefinition',
5455
INPUT_OBJECT_TYPE_DEFINITION = 'InputObjectTypeDefinition',
@@ -64,6 +65,7 @@ export enum Kind {
6465
OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension',
6566
INTERFACE_TYPE_EXTENSION = 'InterfaceTypeExtension',
6667
UNION_TYPE_EXTENSION = 'UnionTypeExtension',
68+
INTERSECTION_TYPE_EXTENSION = 'IntersectionTypeExtension',
6769
ENUM_TYPE_EXTENSION = 'EnumTypeExtension',
6870
INPUT_OBJECT_TYPE_EXTENSION = 'InputObjectTypeExtension',
6971
}

0 commit comments

Comments
 (0)