diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index b539b936e..b2b9cf315 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1051,7 +1051,9 @@ InterfaceTypeDefinition : GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects and interfaces can then implement these interfaces which requires that -the implementing type will define all fields defined by those interfaces. +the implementing type will define all fields defined by those interfaces. Unions +can also implement interfaces, as long as each union member implements those +interfaces. Fields on a GraphQL interface have the same rules as fields on a GraphQL object; their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping @@ -1160,9 +1162,8 @@ interface Resource implements Node { ``` Transitively implemented interfaces (interfaces implemented by the interface -that is being implemented) must also be defined on an implementing type or -interface. For example, `Image` cannot implement `Resource` without also -implementing `Node`: +that is being implemented) must also be defined on the implementing type. For +example, `Image` cannot implement `Resource` without also implementing `Node`: ```raw graphql example interface Node { @@ -1181,6 +1182,8 @@ interface Image implements Resource & Node { } ``` +Similar syntax for unions implementing interfaces is detailed below. + Interface definitions must not contain cyclic references nor implement themselves. This example is invalid because `Node` and `Named` implement themselves and each other: @@ -1293,8 +1296,8 @@ defined. ## Unions -UnionTypeDefinition : Description? union Name Directives[Const]? -UnionMemberTypes? +UnionTypeDefinition : Description? union Name ImplementsInterfaces? +Directives[Const]? UnionMemberTypes? UnionMemberTypes : @@ -1302,16 +1305,17 @@ UnionMemberTypes : - = `|`? NamedType GraphQL Unions represent an object that could be one of a list of GraphQL Object -types, but provides for no guaranteed fields between those types. They also -differ from interfaces in that Object types declare what interfaces they -implement, but are not aware of what unions contain them. +types. They differ from interfaces in that Object types declare what interfaces +they implement, but are not aware of what unions contain them. With interfaces and objects, only those fields defined on the type can be queried directly; to query other fields on an interface, typed fragments must be -used. This is the same as for unions, but unions do not define any fields, so -**no** fields may be queried on this type without the use of type refining -fragments or inline fragments (with the exception of the meta-field -{\_\_typename}). +used. This is the same as for unions, but unions do not directly define any +fields, so the only fields that may be queried on a Union are the meta-field +{\_\_typename} and the fields of the interfaces that the Union declares it +implements (see +[Unions Implementing Interfaces](#sec-Unions.Unions-Implementing-Interfaces)). +Otherwise, type refining fragments or inline fragments must be used. For example, we might define the following types: @@ -1370,6 +1374,66 @@ union SearchResult = | Person ``` +**Unions Implementing Interfaces** + +When defining unions that implement interfaces, each union member must +explicitly implement each interface implemented by the union. For example, the +member types of union SearchResult must each explicitly implement the Resource +interface: + +```raw graphql example +interface Resource { + url: String +} + +union SearchResult implements Resource = Photo | Article + +type Article implements Resource { + url: String + title: String +} + +type Photo implements Resource { + url: String + height: Int + width: Int +} +``` + +The following query would then be valid: + +```graphql example +{ + firstSearchResult { + url + ... on Article { + title + } + ... on Photo { + height + } + } +} +``` + +Transitively implemented interfaces (interfaces implemented by the interface +that is being implemented) must also be defined on the implementing union. For +example, `SearchResult` cannot implement `Resource` without also implementing +`Node`: + +```raw graphql example +interface Node { + id: ID! +} + +interface Resource implements Node { + id: ID! + url: String +} + +union SearchResult implements Resource & Node = Photo | Article +``` + **Result Coercion** The union type should have some way of determining which object a given result @@ -1388,13 +1452,26 @@ Union types have the potential to be invalid if incorrectly defined. 2. The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union. +3. A union type may declare that it implements one or more unique interfaces. +4. Each member of a union must be a super-set of all union-implemented + interfaces. Moreover, each union member must declare that it implements each + interface declared on the union: + 1. Let this union type be {implementingType}. + 2. For each member type of {implementingType}: + 1. Let this member type be {memberType}. + 2. For each interface declared implemented by {implementingType}. + 1. Let the implemented type be {implementedType}. + 2. {IsValidImplementation(memberType, implementedType)} must be {true}. + 3. Let the set of types declared as implemented by {memberType} be + {memberImplementedTypes}. + 4. {memberImplementedTypes} must contain {implementedType}. ### Union Extensions UnionTypeExtension : -- extend union Name Directives[Const]? UnionMemberTypes -- extend union Name Directives[Const] +- extend union Name ImplementsInterfaces? Directives[Const]? UnionMemberTypes +- extend union Name ImplementsInterfaces? Directives[Const] Union type extensions are used to represent a union type which has been extended from some original union type. For example, this might be used to represent @@ -1414,6 +1491,9 @@ Union type extensions have the potential to be invalid if incorrectly defined. the original Union type. 5. Any non-repeatable directives provided must not already apply to the original Union type. +6. All member types of the resulting extended Union type must be a super-set of + all Interfaces it implements. Moreover, each union member must declare that + it implements each interface that declared on the union. ## Enums diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 9b32133f8..5c1f362e4 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -306,6 +306,9 @@ Fields\: - `kind` must return `__TypeKind.UNION`. - `name` must return a String. - `description` may return a String or {null}. +- `fields` must return the set of fields that can be selected for this type. + - Accepts the argument `includeDeprecated` which defaults to {false}. If + {true}, deprecated fields are also returned. - `possibleTypes` returns the list of types that can be represented within this union. They must be object types. - All other fields must return {null}.