Skip to content

Commit 3d4f410

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

6 files changed

+310
-47
lines changed

spec/Appendix B -- Grammar Summary.md

Lines changed: 14 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 199 additions & 11 deletions
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,181 @@ 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+
interface Link {
1454+
link: Downloadable
1455+
}
1456+
1457+
type SearchResultLink implements Link {
1458+
link: DownloadableSearchResult
1459+
}
1460+
1461+
interface Downloadable {
1462+
url: string
1463+
}
1464+
1465+
union SearchResult = Photo | Person
1466+
1467+
intersection DownloadableSearchResult = SearchResult & Downloadable
1468+
1469+
type Person implements Downloadable {
1470+
url: String
1471+
name: String
1472+
age: Int
1473+
}
1474+
1475+
type Photo implements Downloadable {
1476+
url: String
1477+
height: Int
1478+
width: Int
1479+
}
1480+
1481+
type SearchQuery {
1482+
firstDownloadableSearchResult: DownloadableSearchResult
1483+
}
1484+
```
1485+
1486+
Because intersection `DownloadableSearchResult` includes interface
1487+
`Downloadable` as a constraining member type, the possible types of the
1488+
intersection implement the `Downloadable` interface. The `link` field within
1489+
`SearchResultLink` therefore implements the `link` field of interface `Link`.
1490+
1491+
Just as with unions, the below could be ambiguous and is invalid.
1492+
1493+
```graphql counter-example
1494+
{
1495+
firstDownloadableSearchResult {
1496+
url
1497+
name
1498+
height
1499+
}
1500+
}
1501+
```
1502+
1503+
A valid operation includes typed fragments (in this example, inline fragments):
1504+
1505+
```graphql example
1506+
{
1507+
firstDownloadableSearchResult {
1508+
... on Downloadable {
1509+
url
1510+
}
1511+
... on Person {
1512+
name
1513+
}
1514+
... on Photo {
1515+
height
1516+
}
1517+
}
1518+
}
1519+
```
1520+
1521+
Intersection members may be defined with an optional leading `&` character to
1522+
aid formatting when representing a longer list of constraining types:
1523+
1524+
```raw graphql example
1525+
intersection DownloadableSearchResult =
1526+
& Downloadable
1527+
& SearchResult
1528+
```
1529+
1530+
Interfaces that are transitively included within an intersection (interfaces
1531+
implemented by an included interface) must also be included within the
1532+
intersection. For example, `AuthoredPublicContent` cannot include `Authored`
1533+
without also including `Node`:
1534+
1535+
```raw graphql example
1536+
interface Node {
1537+
id: ID!
1538+
}
1539+
1540+
interface Authored implements Node {
1541+
author: String
1542+
}
1543+
1544+
union PublicContent = Document | Image
1545+
1546+
intersection AuthoredPublicContent = PublicContent & Authored & Node
1547+
```
1548+
1549+
**Result Coercion**
1550+
1551+
The intersection type should have some way of determining which object a given
1552+
result corresponds to. Once it has done so, the result coercion of the
1553+
intersection is the same as the result coercion of the object.
1554+
1555+
**Input Coercion**
1556+
1557+
Intersections are never valid inputs.
1558+
1559+
**Type Validation**
1560+
1561+
Intersection types have the potential to be invalid if incorrectly defined.
1562+
1563+
1. An Intersection type must include one or more unique member types.
1564+
2. The member types of a Intersection type must all be Interface or Union base
1565+
types; Scalar, Enum, Object and other Intersection types must not be member
1566+
types of an Intersection. Similarly, wrapping types must not be member types
1567+
of an Intersection.
1568+
3. For each member type of an Intersection type:
1569+
1. Let this member type be {memberType}.
1570+
2. If {memberType} is an interface, all interfaces that {memberType} declares
1571+
it implements must also be member types of the Intersection.
1572+
1573+
### Intersection Extensions
1574+
1575+
IntersectionTypeExtension :
1576+
1577+
- extend intersection Name Directives[Const]? IntersectionMemberTypes
1578+
- extend intersection Name Directives[Const]
1579+
1580+
Intersection type extensions are used to represent an intersection type which
1581+
has been extended from some original intersection type. Similar to unions, this
1582+
might by utilized by a GraphQL service which is itself an extension of another
1583+
GraphQL service.
1584+
1585+
**Type Validation**
1586+
1587+
Intersection type extensions have the potential to be invalid if incorrectly
1588+
defined.
1589+
1590+
1. The named type must already be defined and must be a Intersection type.
1591+
2. The member types of a Intersection type must all be Interface or Union base
1592+
types; Scalar, Enum, Object and other Intersection types must not be member
1593+
types of an Intersection. Similarly, wrapping types must not be member types
1594+
of an Intersection.
1595+
3. For each member type of an Intersection type:
1596+
1. Let this member type be {memberType}.
1597+
2. If {memberType} is an interface, all interfaces that {memberType} declares
1598+
it implements must also be member types of the Intersection.
1599+
4. All member types of an Intersection type extension must be unique.
1600+
5. All member types of an Intersection type extension must not already be a
1601+
member of the original Intersection type.
1602+
6. Any non-repeatable directives provided must not already apply to the original
1603+
Intersection type.
1604+
14181605
## Enums
14191606

14201607
EnumTypeDefinition :
@@ -1876,6 +2063,7 @@ TypeSystemDirectiveLocation : one of
18762063
- `ARGUMENT_DEFINITION`
18772064
- `INTERFACE`
18782065
- `UNION`
2066+
- `INTERSECTION`
18792067
- `ENUM`
18802068
- `ENUM_VALUE`
18812069
- `INPUT_OBJECT`

0 commit comments

Comments
 (0)