Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Allow interfaces to implement other interfaces #373

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
131 changes: 116 additions & 15 deletions spec/Section 3 -- Type System.md
Expand Up @@ -249,9 +249,10 @@ type in the system, allowing the definition of arbitrary type hierarchies.

GraphQL supports two abstract types: interfaces and unions.

An `Interface` defines a list of fields; `Object` types that implement that
interface are guaranteed to implement those fields. Whenever the type system
claims it will return an interface, it will return a valid implementing type.
An `Interface` defines a list of fields; `Object` types and other Interface
types that implement the interface are guaranteed to implement those fields.
leebyron marked this conversation as resolved.
Show resolved Hide resolved
Whenever the type system claims it will return an interface, it will return a
leebyron marked this conversation as resolved.
Show resolved Hide resolved
valid implementing `Object` type.
leebyron marked this conversation as resolved.
Show resolved Hide resolved

A `Union` defines a list of possible types; similar to interfaces, whenever the
type system claims a union will be returned, one of the possible types will be
Expand Down Expand Up @@ -804,23 +805,25 @@ of rules must be adhered to by every Object type in a GraphQL schema.
characters {"__"} (two underscores).
2. The argument must accept a type where {IsInputType(argumentType)}
returns {true}.
4. An object type may declare that it implements one or more unique interfaces.
5. An object type must be a super-set of all interfaces it implements:
3. An object type may declare that it implements one or more unique interfaces.
4. An object type must be a super-set of all interfaces it implements:
1. The object type must include a field of the same name for every field
defined in an interface.
1. The object field must be of a type which is equal to or a sub-type of
the interface field (covariant).
1. An object field type is a valid sub-type if it is equal to (the same
type as) the interface field type.
2. An object field type is a valid sub-type if it is an Object type and
the interface field type is either an Interface type or a Union type
and the object field type is a possible type of the interface field
type.
3. An object field type is a valid sub-type if it is a List type and
the interface field type is a Union type and the object field type
is a possible type of the interface field type.
leebyron marked this conversation as resolved.
Show resolved Hide resolved
3. An object field type is a valid sub-type if it is an Object or
Interface type and the interface field type is an Interface type and
the object field type implements the interface field type.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the clarity from breaking this rule out

4. An object field type is a valid sub-type if it is a List type and
the interface field type is also a List type and the list-item type
of the object field type is a valid sub-type of the list-item type
of the interface field type.
4. An object field type is a valid sub-type if it is a Non-Null variant
5. An object field type is a valid sub-type if it is a Non-Null variant
of a valid sub-type of the interface field type.
2. The object field must include an argument of the same name for every
argument defined in the interface field.
Expand All @@ -829,6 +832,9 @@ of rules must be adhered to by every Object type in a GraphQL schema.
3. The object field may include additional arguments not defined in the
interface field, but any additional argument must not be required, e.g.
must not be of a non-nullable type.
5. If an object type declares that it implements one or more interfaces that
implement other interfaces, it must also include each transitively
implemented interface in its list of implemented interfaces.
leebyron marked this conversation as resolved.
Show resolved Hide resolved


### Field Arguments
Expand Down Expand Up @@ -947,8 +953,8 @@ Object type extensions have the potential to be invalid if incorrectly defined.
InterfaceTypeDefinition : Description? interface Name Directives[Const]? FieldsDefinition?

GraphQL interfaces represent a list of named fields and their arguments. GraphQL
objects can then implement these interfaces which requires that the object type
will define all fields defined by those interfaces.
objects and interfaces can then implement these interfaces which requires that
the implementing type will define all fields defined by 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
Expand Down Expand Up @@ -1038,6 +1044,62 @@ interface. Querying for `age` is only valid when the result of `entity` is a
}
```

**Interfaces Implementing Interfaces**

When defining an interface that implements another interface, the implementing
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved
interface must define each field that is specified by the implemented interface.
For example, the interface Resource must define the field id to implement the
Node interface:

```graphql example
interface Node {
id: ID!
}

interface Resource implements Node {
id: ID!
url: String
}
```

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`:

```graphql example
interface Node {
id: ID!
}

interface Resource implements Node {
id: ID!
url: String
}

interface Image implements Resource & Node {
id: ID!
url: String
thumbnail: String
}
```

Interfaces definitions must not contain cyclic references. This example is
invalid because Node and Named implement each other.

```graphgl counter-example
interface Node implements Named {
id: ID!
name: String
}

interface Named implementes Node {
id: ID!
name: String
}
```


**Result Coercion**

The interface type should have some way of determining which object a given
Expand Down Expand Up @@ -1065,6 +1127,43 @@ Interface types have the potential to be invalid if incorrectly defined.
characters {"__"} (two underscores).
2. The argument must accept a type where {IsInputType(argumentType)}
returns {true}.
3. An interface type may declare that it implements one or more unique
interfaces, but may not implement itself.
4. An interface type must be a super-set of all interfaces it implements:
1. The implementing interface type must include a field of the same name for
every field defined in an implemented interface.
1. The implementing interface field must be of a type which is equal to or
a sub-type of the implemented interface field (covariant).
1. An implementing interface field type is a valid sub-type if it is
equal to (the same type as) the implemented interface field type.
2. An implementing interface field type is a valid sub-type if it is an
Object type and the implemented interface field type is a Union type
and the implementing interface field type is a possible type of the
implemented interface field type.
3. An implementing interface field type is a valid sub-type if it is an
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved
Object or Interface type and the implemented interface field type is
an Interface type and the implementing interface field type
implements the implemented interface field type.
4. An implementing interface field type is a valid sub-type if it is a
List type and the implemented interface field type is also a List
type and the list-item type of the implementing interface field type
is a valid sub-type of the list-item type of the implemented
interface field type.
5. An implementing interface field type is a valid sub-type if it is a
Non-Null variant of a valid sub-type of the implemented interface
field type.
2. The implementing interface field must include an argument of the same
name for every argument defined in the implemented interface field.
1. The implementing interface field argument must accept the same type
(invariant) as the implemented interface field argument.
3. The implementing interface field may include additional arguments not
defined in the implemented interface field, but any additional argument
must not be required.
leebyron marked this conversation as resolved.
Show resolved Hide resolved
5. If an interface type declares that it implements one or more interfaces that
implement other interfaces, it must also include each transitively
implemented interface in its list of implemented interfaces.
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved
6. An interface cannot implement an interface that in turn implements the
original interface.
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved


### Interface Extensions
Expand Down Expand Up @@ -1114,10 +1213,12 @@ Interface type extensions have the potential to be invalid if incorrectly define
fields may share the same name.
3. Any fields of an Interface type extension must not be already defined on the
original Interface type.
4. Any Object type which implemented the original Interface type must also be a
super-set of the fields of the Interface type extension (which may be due to
Object type extension).
4. Any Object or Interface type which implemented the original Interface type
must also be a super-set of the fields of the Interface type extension (which
may be due to Object type extension).
5. Any directives provided must not already apply to the original Interface type.
6. The resulting extended interface type must be a super-set of all interfaces
it implements.


## Unions
Expand Down
3 changes: 2 additions & 1 deletion spec/Section 4 -- Introspection.md
Expand Up @@ -132,7 +132,7 @@ type __Type {
# should be non-null for OBJECT and INTERFACE only, must be null for the others
fields(includeDeprecated: Boolean = false): [__Field!]

# should be non-null for OBJECT only, must be null for the others
# should be non-null for OBJECT and INTERFACE only, must be null for the others
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved
interfaces: [__Type!]

# should be non-null for INTERFACE and UNION only, always null for the others
Expand Down Expand Up @@ -291,6 +291,7 @@ Fields
* `fields`: The set of fields required by this interface.
* Accepts the argument `includeDeprecated` which defaults to {false}. If
{true}, deprecated fields are also returned.
* `interfaces`: The set of interfaces that an interface implements.
leebyron marked this conversation as resolved.
Show resolved Hide resolved
* `possibleTypes` returns the list of types that implement this interface.
mike-marcacci marked this conversation as resolved.
Show resolved Hide resolved
They must be object types.
* All other fields must return {null}.
Expand Down
6 changes: 3 additions & 3 deletions spec/Section 5 -- Validation.md
Expand Up @@ -1132,7 +1132,7 @@ fragment catInDogFragmentInvalid on Dog {
##### Abstract Spreads in Object Scope

In scope of an object type, unions or interface spreads can be used
if the object type implements the interface or is a member of the union.
if the type implements the interface or is a member of the union.
leebyron marked this conversation as resolved.
Show resolved Hide resolved

For example

Expand Down Expand Up @@ -1172,8 +1172,8 @@ declaration, not its body.
##### Object Spreads In Abstract Scope

Union or interface spreads can be used within the context of an object type
fragment, but only if the object type is one of the possible types of
that interface or union.
fragment or interface type fragment, but only if the type is one of the
leebyron marked this conversation as resolved.
Show resolved Hide resolved
possible types of that interface or union.

For example, the following fragments are valid:

Expand Down