Skip to content

Commit 3a92f7c

Browse files
committed
introduce new intersection type
see graphql/graphql-js#3550
1 parent 78ccda7 commit 3a92f7c

6 files changed

+270
-47
lines changed

spec/Appendix B -- Grammar Summary.md

+14
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,19 @@ UnionTypeExtension :
343343
- extend union Name Directives[Const]? UnionMemberTypes
344344
- extend union Name Directives[Const]
345345

346+
IntersectionTypeDefinition : Description? intersection Name Directives[Const]?
347+
IntersectionMemberTypes?
348+
349+
UnionMemberTypes :
350+
351+
- IntersectionMemberTypes | NamedType
352+
- = `|`? NamedType
353+
354+
IntersectionTypeExtension :
355+
356+
- extend intersection Name Directives[Const]? IntersectionMemberTypes
357+
- extend intersection Name Directives[Const]
358+
346359
EnumTypeDefinition :
347360

348361
- Description? enum Name Directives[Const]? EnumValuesDefinition
@@ -402,6 +415,7 @@ TypeSystemDirectiveLocation : one of
402415
- `ARGUMENT_DEFINITION`
403416
- `INTERFACE`
404417
- `UNION`
418+
- `INTERSECTION`
405419
- `ENUM`
406420
- `ENUM_VALUE`
407421
- `INPUT_OBJECT`

spec/Section 2 -- Language.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,8 @@ Fragments are the primary unit of composition in GraphQL.
528528

529529
Fragments allow for the reuse of common repeated selections of fields, reducing
530530
duplicated text in the document. Inline Fragments can be used directly within a
531-
selection to condition upon a type condition when querying against an interface
532-
or union.
531+
selection to condition upon a type condition when querying against an interface,
532+
union, or intersection.
533533

534534
For example, if we wanted to fetch some common information about mutual friends
535535
as well as friends of some user:

spec/Section 3 -- Type System.md

+159-11
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ TypeDefinition :
261261
- ObjectTypeDefinition
262262
- InterfaceTypeDefinition
263263
- UnionTypeDefinition
264+
- IntersectionTypeDefinition
264265
- EnumTypeDefinition
265266
- InputObjectTypeDefinition
266267

@@ -276,7 +277,7 @@ Scalars and Enums form the leaves in response trees; the intermediate levels are
276277
`Object` types, which define a set of fields, where each field is another type
277278
in the system, allowing the definition of arbitrary type hierarchies.
278279

279-
GraphQL supports two abstract types: interfaces and unions.
280+
GraphQL supports three abstract types: interfaces, unions and intersections.
280281

281282
An `Interface` defines a list of fields; `Object` types and other Interface
282283
types which implement this Interface are guaranteed to implement those fields.
@@ -287,6 +288,11 @@ A `Union` defines a list of possible types; similar to interfaces, whenever the
287288
type system claims a union will be returned, one of the possible types will be
288289
returned.
289290

291+
An `Intersection` defines a list of constraining abstract types. If a field
292+
claims it returns an Intersection type, it will return only types that are
293+
contained within all of the Intersections's Unions and types that implement all
294+
of the Intersection's interfaces.
295+
290296
Finally, oftentimes it is useful to provide complex structs as inputs to GraphQL
291297
field arguments or variables; the `Input Object` type allows the schema to
292298
define exactly what data is expected.
@@ -314,9 +320,9 @@ to arguments and variables as well as the values output by fields. These two
314320
uses categorize types as _input types_ and _output types_. Some kinds of types,
315321
like Scalar and Enum types, can be used as both input types and output types;
316322
other kinds of types can only be used in one or the other. Input Object types
317-
can only be used as input types. Object, Interface, and Union types can only be
318-
used as output types. Lists and Non-Null types may be used as input types or
319-
output types depending on how the wrapped type may be used.
323+
can only be used as input types. Object, Interface, Union, and Intersection
324+
types can only be used as output types. Lists and Non-Null types may be used as
325+
input types or output types depending on how the wrapped type may be used.
320326

321327
IsInputType(type) :
322328

@@ -332,7 +338,7 @@ IsOutputType(type) :
332338
- If {type} is a List type or Non-Null type:
333339
- Let {unwrappedType} be the unwrapped type of {type}.
334340
- Return IsOutputType({unwrappedType})
335-
- If {type} is a Scalar, Object, Interface, Union, or Enum type:
341+
- If {type} is a Scalar, Object, Interface, Union, Intersection, or Enum type:
336342
- Return {true}
337343
- Return {false}
338344

@@ -706,8 +712,8 @@ Must only yield exactly that subset:
706712
```
707713

708714
A field of an Object type may be a Scalar, Enum, another Object type, an
709-
Interface, or a Union. Additionally, it may be any wrapping type whose
710-
underlying base type is one of those five.
715+
Interface, a Union, or an Intersection. Additionally, it may be any wrapping
716+
type whose underlying base type is one of those five.
711717

712718
For example, the `Person` type might include a `relationship`:
713719

@@ -928,7 +934,13 @@ IsValidImplementationFieldType(fieldType, implementedFieldType):
928934
5. If {fieldType} is an Object or Interface type and {implementedFieldType} is
929935
an Interface type and {fieldType} declares it implements
930936
{implementedFieldType} then return {true}.
931-
6. Otherwise return {false}.
937+
6. If {fieldType} is an IntersectionType and {implementedFieldType} is an Union
938+
type and {fieldType} is a member type of {implementedFieldType} then return
939+
{true}.
940+
7. If {fieldType} is an IntersectionType and {implementedFieldType} is an
941+
Interface type and at least one of the members of {fieldType} declares it
942+
implements {implementedFieldType} then return {true}.
943+
8. Otherwise return {false}.
932944

933945
### Field Arguments
934946

@@ -977,7 +989,7 @@ May return the result:
977989
```
978990

979991
The type of an object field argument must be an input type (any type except an
980-
Object, Interface, or Union type).
992+
Object, Interface, Union, or Intersection type).
981993

982994
### Field Deprecation
983995

@@ -1054,8 +1066,8 @@ objects and interfaces can then implement these interfaces which requires that
10541066
the implementing type will define all fields defined by those interfaces.
10551067

10561068
Fields on a GraphQL interface have the same rules as fields on a GraphQL object;
1057-
their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping
1058-
type whose base type is one of those five.
1069+
their type can be Scalar, Object, Enum, Interface, Union, or Intersection, or
1070+
any wrapping type whose base type is one of those five.
10591071

10601072
For example, an interface `NamedEntity` may describe a required field and types
10611073
such as `Person` or `Business` may then implement this interface to guarantee
@@ -1415,6 +1427,141 @@ Union type extensions have the potential to be invalid if incorrectly defined.
14151427
5. Any non-repeatable directives provided must not already apply to the original
14161428
Union type.
14171429

1430+
## Intersections
1431+
1432+
IntersectionTypeDefinition : Description? intersection Name Directives[Const]?
1433+
IntersectionMemberTypes?
1434+
1435+
IntersectionMemberTypes :
1436+
1437+
- IntersectionMemberTypes | NamedType
1438+
- = `|`? NamedType
1439+
1440+
GraphQL Intersections are higher order abstract types that represent objects
1441+
satisfying the requirements of all of the Intersection's abstract member types.
1442+
Intersections combine the features of interfaces and unions; the objects
1443+
represented by an Intersection must implement all of the Intersection's
1444+
interfaces and must also be included within all of its unions.
1445+
1446+
Just as with unions, intersections do not directly define any fields, so **no**
1447+
fields may be queried on this type without the use of type refining fragments or
1448+
inline fragments (with the exception of the meta-field {\_\_typename}).
1449+
1450+
For example, we might define the following types:
1451+
1452+
```graphql example
1453+
union SearchResult = Photo | Person
1454+
1455+
interface Downloadable = {
1456+
url: String
1457+
}
1458+
1459+
intersection DownloadableSearchResult = SearchResult & Downloadable
1460+
1461+
type Person implements Downloadable {
1462+
url: String
1463+
name: String
1464+
age: Int
1465+
}
1466+
1467+
type Photo implements Downloadable {
1468+
url: String
1469+
height: Int
1470+
width: Int
1471+
}
1472+
1473+
type SearchQuery {
1474+
firstDownloadableSearchResult: DownloadableSearchResult
1475+
}
1476+
```
1477+
1478+
Just as with unions, the below could be ambiguous and is invalid.
1479+
1480+
```graphql counter-example
1481+
{
1482+
firstDownloadableSearchResult {
1483+
url
1484+
name
1485+
height
1486+
}
1487+
}
1488+
```
1489+
1490+
A valid operation includes typed fragments (in this example, inline fragments):
1491+
1492+
```graphql example
1493+
{
1494+
firstDownloadableSearchResult {
1495+
... on Downloadable {
1496+
url
1497+
}
1498+
... on Person {
1499+
name
1500+
}
1501+
... on Photo {
1502+
height
1503+
}
1504+
}
1505+
}
1506+
```
1507+
1508+
Intersection members may be defined with an optional leading `&` character to
1509+
aid formatting when representing a longer list of constraining types:
1510+
1511+
```raw graphql example
1512+
intersection DownloadableSearchResult =
1513+
& Downloadable
1514+
& SearchResult
1515+
```
1516+
1517+
**Result Coercion**
1518+
1519+
The intersection type should have some way of determining which object a given
1520+
result corresponds to. Once it has done so, the result coercion of the
1521+
intersection is the same as the result coercion of the object.
1522+
1523+
**Input Coercion**
1524+
1525+
Intersections are never valid inputs.
1526+
1527+
**Type Validation**
1528+
1529+
Intersection types have the potential to be invalid if incorrectly defined.
1530+
1531+
1. An Intersection type must include one or more unique member types.
1532+
2. The member types of a Intersection type must all be Interface, Union or
1533+
Intersection base types; Scalar, Enum and Object types must not be member
1534+
types of an an Intersection. Similarly, wrapping types must not be member
1535+
types of an Intersection.
1536+
1537+
### Intersection Extensions
1538+
1539+
IntersectionTypeExtension :
1540+
1541+
- extend intersection Name Directives[Const]? IntersectionMemberTypes
1542+
- extend intersection Name Directives[Const]
1543+
1544+
Intersection type extensions are used to represent an intersection type which
1545+
has been extended from some original intersection type. Similar to unions, this
1546+
might by utilized by a GraphQL service which is itself an extension of another
1547+
GraphQL service.
1548+
1549+
**Type Validation**
1550+
1551+
Intersection type extensions have the potential to be invalid if incorrectly
1552+
defined.
1553+
1554+
1. The named type must already be defined and must be a Intersection type.
1555+
2. The member types of a Intersection type must all be Interface, Union or
1556+
Intersection base types; Scalar, Enum and Object types must not be member
1557+
types of an an Intersection. Similarly, wrapping types must not be member
1558+
types of an Intersection.
1559+
3. All member types of an Intersection type extension must be unique.
1560+
4. All member types of an Intersection type extension must not already be a
1561+
member of the original Intersection type.
1562+
5. Any non-repeatable directives provided must not already apply to the original
1563+
Intersection type.
1564+
14181565
## Enums
14191566

14201567
EnumTypeDefinition :
@@ -1876,6 +2023,7 @@ TypeSystemDirectiveLocation : one of
18762023
- `ARGUMENT_DEFINITION`
18772024
- `INTERFACE`
18782025
- `UNION`
2026+
- 'INTERSECTION'
18792027
- `ENUM`
18802028
- `ENUM_VALUE`
18812029
- `INPUT_OBJECT`

spec/Section 4 -- Introspection.md

+31-5
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,12 @@ underscores {"\_\_"}.
7171
GraphQL supports type name introspection within any selection set in an
7272
operation, with the single exception of selections at the root of a subscription
7373
operation. Type name introspection is accomplished via the meta-field
74-
`__typename: String!` on any Object, Interface, or Union. It returns the name of
75-
the concrete Object type at that point during execution.
74+
`__typename: String!` on any Object, Interface, Union, Intersection. It returns
75+
the name of the concrete Object type at that point during execution.
7676

77-
This is most often used when querying against Interface or Union types to
78-
identify which actual Object type of the possible types has been returned.
77+
This is most often used when querying against Interface, Union, or Intersection
78+
types to identify which actual Object type of the possible types has been
79+
returned.
7980

8081
As a meta-field, `__typename` is implicit and does not appear in the fields list
8182
in any defined type.
@@ -140,7 +141,9 @@ type __Type {
140141
fields(includeDeprecated: Boolean = false): [__Field!]
141142
# must be non-null for OBJECT and INTERFACE, otherwise null.
142143
interfaces: [__Type!]
143-
# must be non-null for INTERFACE and UNION, otherwise null.
144+
# must be non-null for INTERSECTION, otherwise null.
145+
memberTypes: [__Type!]
146+
# must be non-null for INTERFACE, UNION, and INTERSECTION, otherwise null.
144147
possibleTypes: [__Type!]
145148
# must be non-null for ENUM, otherwise null.
146149
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
@@ -157,6 +160,7 @@ enum __TypeKind {
157160
OBJECT
158161
INTERFACE
159162
UNION
163+
INTERSECTION
160164
ENUM
161165
INPUT_OBJECT
162166
LIST
@@ -210,6 +214,7 @@ enum __DirectiveLocation {
210214
ARGUMENT_DEFINITION
211215
INTERFACE
212216
UNION
217+
INTERSECTION
213218
ENUM
214219
ENUM_VALUE
215220
INPUT_OBJECT
@@ -256,6 +261,7 @@ possible value of the `__TypeKind` enum:
256261
- {"OBJECT"}
257262
- {"INTERFACE"}
258263
- {"UNION"}
264+
- {"INTERSECTION"}
259265
- {"ENUM"}
260266
- {"INPUT_OBJECT"}
261267
- {"LIST"}
@@ -310,6 +316,25 @@ Fields\:
310316
union. They must be object types.
311317
- All other fields must return {null}.
312318

319+
**Intersection**
320+
321+
Intersections are a higher-order abstract type defined by a list of constraining
322+
abstract types. The list of constraining abstract member types is accessible in
323+
`memberTypes`. The possible types of an intersection are explicitly listed out
324+
in `possibleTypes`. Types can be made parts of intersections without
325+
modification of that type.
326+
327+
Fields\:
328+
329+
- `kind` must return `__TypeKind.INTERSECTION`.
330+
- `name` must return a String.
331+
- `description` may return a String or {null}.
332+
- `memberTypes` returns the list of constraining abstract types defined by the
333+
intersection. They must be interface, union, or intersection types.
334+
- `possibleTypes` returns the list of types that can be represented within this
335+
intersection. They must be object types.
336+
- All other fields must return {null}.
337+
313338
**Interface**
314339

315340
Interfaces are an abstract type where there are common fields declared. Any type
@@ -470,6 +495,7 @@ supported. All possible locations are listed in the `__DirectiveLocation` enum:
470495
- {"ARGUMENT_DEFINITION"}
471496
- {"INTERFACE"}
472497
- {"UNION"}
498+
- {"INTERSECTION"}
473499
- {"ENUM"}
474500
- {"ENUM_VALUE"}
475501
- {"INPUT_OBJECT"}

0 commit comments

Comments
 (0)