Skip to content

Editorial: Type kinds #970

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions spec/Section 2 -- Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,11 @@ piece of information available to request within a selection set.

Some fields describe complex data or relationships to other data. In order to
further explore this data, a field may itself contain a selection set, allowing
for deeply nested requests. All GraphQL operations must specify their selections
down to fields which return scalar values to ensure an unambiguously shaped
response.
for deeply nested requests.

:: A _leaf field_ is a field whose unwrapped type is a Scalar or Enum _leaf
type_. All GraphQL operations must specify their selections down to leaf fields
to ensure an unambiguously shaped response.

For example, this operation selects fields of complex data and relationships
down to scalar values.
Expand Down
86 changes: 52 additions & 34 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,32 +264,43 @@ TypeDefinition :
- EnumTypeDefinition
- InputObjectTypeDefinition

The fundamental unit of any GraphQL Schema is the type. There are six kinds of
The fundamental unit of any GraphQL schema is the type. There are six kinds of
named type definitions in GraphQL, and two wrapping types.

The most basic type is a `Scalar`. A scalar represents a primitive value, like a
string or an integer. Oftentimes, the possible responses for a scalar field are
enumerable. GraphQL offers an `Enum` type in those cases, where the type
specifies the space of valid responses.
:: A _leaf type_ is a kind of type representing a primitive value which cannot
be further selected and thus form the leaves in a response tree. GraphQL
Copy link
Contributor

Choose a reason for hiding this comment

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

something does not match; if subject is 'kind' (singular) then the verb should be 'forms' (singular)

Copy link
Contributor

@rivantsov rivantsov Jun 28, 2022

Choose a reason for hiding this comment

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

General suggestion
I think 'kind of type' is confusing, since we have already TYPE_KIND, different kind. What we introduce here are groups of types, sort of, purely for easier references in the spec; these groups or categories do not appear anywhere at runtime or in graphql artifacts - purely for shorter and simpler spec references.

So instead of
A Leaf type is a kind of type representing ...

we can say simply
Leaf types are types representing

without even explicitly calling it a group or category

(Edit) PS - actually this way (without kind) is phrased in other cases, so we would need to just change this spot.

Comment on lines +270 to +271
Copy link
Member

Choose a reason for hiding this comment

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

A bit wordy, but...

Suggested change
:: A _leaf type_ is a kind of type representing a primitive value which cannot
be further selected and thus form the leaves in a response tree. GraphQL
:: A _leaf type_ is a kind of type representing a primitive value which cannot
be further selected, thus the type of each leaf in a response tree is a leaf type. GraphQL

provides two kinds of leaf types: `Scalar` and `Enum`.

Scalars and Enums form the leaves in response trees; the intermediate levels are
`Object` types, which define a set of fields, where each field is another type
in the system, allowing the definition of arbitrary type hierarchies.
A `Scalar` represents a primitive scalar value, such as a string or number.
Oftentimes, the possible responses for a scalar field are enumerable. GraphQL
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Oftentimes, the possible responses for a scalar field are enumerable. GraphQL
Oftentimes, the possible responses for a leaf field are enumerable. GraphQL

offers an `Enum` type in those cases, where the type specifies the set of valid
responses.

GraphQL supports two abstract types: interfaces and unions.
:: A _composite type_ is a type composed of other types via a set of named
Copy link
Contributor

Choose a reason for hiding this comment

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

need consistency in capitalizing - if 'Leaf type' then 'Composite type'

fields. Each field may provide a _leaf type_ or another composite type (or
wrapped types of either), allowing for the definition of arbitrary type
hierarchies.

An `Interface` defines a list of fields; `Object` types and other Interface
types which implement this Interface are guaranteed to implement those fields.
Whenever a field claims it will return an Interface type, it will return a valid
implementing Object type during execution.
An `Object` type is a _composite type_ representing composite values selectable
within a GraphQL operation. They provide the intermediate levels of a schema,
allowing GraphQL to describe an interconnected graph of information.

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
returned.
:: An _abstract type_ allows a GraphQL schema to introduce polymorphism; where a
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it should be comma after polymorphism, not semicolon.

field may provide one of many possible types at runtime. GraphQL provides two
kinds of abstract types: `Interface` and `Union`.

An `Interface` defines a list of fields; `Object` types and other `Interface`
types which implement this `Interface` are guaranteed to implement those fields.
Whenever a field claims it will return an `Interface` type, it will return a
valid implementing `Object` type during execution.

A `Union` defines a list of possible types. Similar to `Interfaces`, whenever a
field claims it will return a `Union` type, it will return one of the possible
`Object` types during execution.

Finally, oftentimes it is useful to provide complex structs as inputs to GraphQL
field arguments or variables; the `Input Object` type allows the schema to
define exactly what data is expected.
field arguments or variables; the `Input Object` type is a _composite type_
which allows the schema to define more complex expected input data.

### Wrapping Types

Expand Down Expand Up @@ -635,14 +646,14 @@ FieldDefinition : Description? Name ArgumentsDefinition? : Type
Directives[Const]?

GraphQL operations are hierarchical and composed, describing a tree of
information. While Scalar types describe the leaf values of these hierarchical
information. While _leaf types_ describe the leaf values of these hierarchical
operations, Objects describe the intermediate levels.
Copy link
Member

Choose a reason for hiding this comment

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

Objects (and lists) descr...


GraphQL Objects represent a list of named fields, each of which yield a value of
a specific type. Object values should be serialized as ordered maps, where the
selected field names (or aliases) are the keys and the result of evaluating the
field is the value, ordered by the order in which they appear in the selection
set.
GraphQL Objects are a _composite type_ representing a list of named fields, each
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
GraphQL Objects are a _composite type_ representing a list of named fields, each
GraphQL Objects are a _composite type_ comprised of a list of named fields, each

of which yield a value of a specific type. Object values should be serialized as
ordered maps, where the selected field names (or aliases) are the keys and the
result of evaluating the field is the value, ordered by the order in which they
appear in the selection set.
Copy link
Contributor

Choose a reason for hiding this comment

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

we might mention here that field names might repeat in this output map, as a result of field merging


All fields defined within an Object type must not have a name which begins with
{"\_\_"} (two underscores), as this is used exclusively by GraphQL's
Expand Down Expand Up @@ -1052,9 +1063,14 @@ InterfaceTypeDefinition :
- Description? interface Name ImplementsInterfaces? Directives[Const]?
[lookahead != `{`]

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.
GraphQL interfaces are an _abstract type_ which when used as the type of a field
provides polymorphism where any implementation may be a possible type during
execution.
Comment on lines +1066 to +1068
Copy link
Member

Choose a reason for hiding this comment

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

Previously it was stated that an interface may implement an interface. So "any implementation" could be perceived as including object types and interfaces. Let's clarify:

Suggested change
GraphQL interfaces are an _abstract type_ which when used as the type of a field
provides polymorphism where any implementation may be a possible type during
execution.
GraphQL interfaces are an _abstract type_ which when used as the type of a field
provides polymorphism where any implementing object type may be a possible type during
execution.


Interfaces are also a _composite type_ as they represent a list of named fields
and their arguments. An object or interface can declare that it implements an
interface which requires that the implementing type will define all fields
defined by that interface.

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 @@ -1304,17 +1320,18 @@ UnionMemberTypes :
- UnionMemberTypes | NamedType
- = `|`? 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.
GraphQL Unions are an _abstract type_ representing one of a list of GraphQL
Object possible types, but provides for no guaranteed fields between those
Copy link
Contributor

Choose a reason for hiding this comment

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

provides -> provide ? since Unions (plural)

types. They also differ from interfaces in that Object types declare what
Copy link
Member

Choose a reason for hiding this comment

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

? Feel free to dismiss.

Suggested change
types. They also differ from interfaces in that Object types declare what
types (other than introspection fields). They also differ from interfaces in that Object types declare which

Definitely what -> which though?

Suggested change
types. They also differ from interfaces in that Object types declare what
types. They also differ from interfaces in that Object types declare which

interfaces they implement, but are not aware of what unions contain them.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
interfaces they implement, but are not aware of what unions contain them.
interfaces they implement, but are not aware of which 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}).
{\_\_typename}). Despite this, a union is still considered a _composite type_ as
it cannot represent a _leaf type_.

For example, we might define the following types:

Expand Down Expand Up @@ -1429,8 +1446,9 @@ EnumValuesDefinition : { EnumValueDefinition+ }

EnumValueDefinition : Description? EnumValue Directives[Const]?

GraphQL Enum types, like Scalar types, also represent leaf values in a GraphQL
type system. However Enum types describe the set of possible values.
GraphQL Enum types, like Scalar types, are a _leaf type_ which represents a
_leaf field_ in a GraphQL type system. However Enum types describe the set of
Comment on lines +1449 to +1450
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
GraphQL Enum types, like Scalar types, are a _leaf type_ which represents a
_leaf field_ in a GraphQL type system. However Enum types describe the set of
GraphQL Enum types, like Scalar types, are a _leaf type_ which can be the unwrapped type of a
_leaf field_ in a GraphQL type system. However Enum types describe the set of

Or simply:

Suggested change
GraphQL Enum types, like Scalar types, are a _leaf type_ which represents a
_leaf field_ in a GraphQL type system. However Enum types describe the set of
A GraphQL Enum type, like Scalar type, is a _leaf type_. However Enum types describe the set of

possible values.

Enums are not references for a numeric value, but are unique values in their own
right. They may serialize as a string: the name of the represented value.
Expand Down
16 changes: 8 additions & 8 deletions spec/Section 4 -- Introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ Fields\:

**Union**

Unions are an abstract type where no common fields are declared. The possible
Unions are an _abstract type_ where no common fields are declared. The possible
types of a union are explicitly listed out in `possibleTypes`. Types can be made
parts of unions without modification of that type.
Comment on lines 304 to 305
Copy link
Member

Choose a reason for hiding this comment

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

Clarify:

Suggested change
types of a union are explicitly listed out in `possibleTypes`. Types can be made
parts of unions without modification of that type.
types of a union are explicitly listed out in `possibleTypes`. An object type can be made a
member of a union without modification of the object type.


Expand All @@ -315,10 +315,10 @@ Fields\:

**Interface**

Interfaces are an abstract type where there are common fields declared. Any type
that implements an interface must define all the fields with names and types
exactly matching. The implementations of this interface are explicitly listed
out in `possibleTypes`.
Interfaces are an _abstract type_ where there are common fields declared. Any
type that implements an interface must define all the fields with names and
types exactly matching. The implementations of this interface are explicitly
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not think we require exact matching of type for implementing field. It can be derived/compatible type
Like interface declares:
boo : IBoo
while implementing type can declare
boo: Boo # implements IBoo

Comment on lines +319 to +320
Copy link
Member

Choose a reason for hiding this comment

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

Clarify only the object types are in possibleTypes, and that types must be compatible.

Suggested change
type that implements an interface must define all the fields with names and
types exactly matching. The implementations of this interface are explicitly
type that implements an interface must define all the fields with matching names and
compatible types. The object types implementing this interface are explicitly

listed out in `possibleTypes`.

Fields\:

Expand Down Expand Up @@ -351,9 +351,9 @@ Fields\:

**Input Object**

Input objects are composite types defined as a list of named input values. They
are only used as inputs to arguments and variables and cannot be a field return
type.
Input Objects are a _composite type_ defined as a list of named input field
values. They are only used as inputs to arguments and variables and cannot be a
field return type.
Comment on lines +354 to +356
Copy link
Member

Choose a reason for hiding this comment

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

Want to make it clear that input objects can be comprised of other input objects, and thus they are not only the full input to arguments but also parts thereof. I'm not sure if dropping the s conveys this subtlety, but...

Suggested change
Input Objects are a _composite type_ defined as a list of named input field
values. They are only used as inputs to arguments and variables and cannot be a
field return type.
An Input Object is a _composite type_ defined as a list of named input field
values. They are only used as input to arguments and variables and cannot be a
field return type.


For example the input object `Point` could be defined as:

Expand Down
28 changes: 15 additions & 13 deletions spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ SameResponseShape(fieldA, fieldB):
- If {typeA} or {typeB} is Scalar or Enum:
- If {typeA} and {typeB} are the same type return true, otherwise return
false.
- Assert: {typeA} and {typeB} are both composite types.
- Assert: {typeA} and {typeB} are both Object, Interface or Union.
Copy link
Member

Choose a reason for hiding this comment

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

This could be considered as (a is object and b is object) or (a is interface and b is interface) or (a is union and b is union). I think it's clearer without this change; alternatively use "each":

Suggested change
- Assert: {typeA} and {typeB} are both Object, Interface or Union.
- Assert: each of {typeA} and {typeB} are an Object, Interface or Union.

- Let {mergedSet} be the result of adding the selection set of {fieldA} and the
selection set of {fieldB}.
- Let {fieldsForName} be the set of selections with a given response name in
Expand Down Expand Up @@ -555,8 +555,8 @@ fragment safeDifferingArgs on Pet {
}
```

However, the field responses must be shapes which can be merged. For example,
scalar values must not differ. In this example, `someValue` might be a `String`
However, the field responses must be shapes which can be merged, and _leaf
field_ types must not differ. In this example, `someValue` might be a `String`
or an `Int`:

```graphql counter-example
Expand All @@ -583,8 +583,8 @@ fragment conflictingDifferingResponses on Pet {

**Explanatory Text**

A field subselection is not allowed on leaf fields. A leaf field is any field
with a scalar or enum unwrapped type.
A field subselection is not allowed on a _leaf field_, a field with a Scalar or
Enum unwrapped type.
Comment on lines +586 to +587
Copy link
Member

Choose a reason for hiding this comment

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

Use parenthesis to make it clear this isn't a list:

Suggested change
A field subselection is not allowed on a _leaf field_, a field with a Scalar or
Enum unwrapped type.
A field subselection is not allowed on a _leaf field_ (a field with a Scalar or
Enum unwrapped type).


The following is valid.

Expand Down Expand Up @@ -1145,8 +1145,9 @@ 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.
In scope of an object type, a fragment spread of an _abstract type_ (interface
or union) can be used if the object type is one of the possible types of that
abstract type (it implements the interface or is a member of the union).

For example

Expand Down Expand Up @@ -1183,9 +1184,9 @@ invalid because we only consider the fragment 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.
In the scope of an _abstract type_ (interface or union), a fragment spread of an
object type can be used if the object type is one of the possible types of that
abstract type (it implements the interface or is a member of the union).

For example, the following fragments are valid:

Expand Down Expand Up @@ -1230,9 +1231,10 @@ can also never return meaningful results, making it invalid.

##### Abstract Spreads in Abstract Scope

Union or interfaces fragments can be used within each other. As long as there
exists at least _one_ object type that exists in the intersection of the
possible types of the scope and the spread, the spread is considered valid.
In the scope of an _abstract type_ (interface or union), a fragment spread of
another _abstract type_ can be used as long as there exists at least _one_
Copy link
Member

Choose a reason for hiding this comment

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

Emphasis unnecessary?

Suggested change
another _abstract type_ can be used as long as there exists at least _one_
another _abstract type_ can be used as long as there exists at least one

object type that exists in the intersection of the possible types of the scope
Copy link
Contributor

Choose a reason for hiding this comment

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

there exists... that exists ...

suggest to rephrase

and the spread.

So for example

Expand Down
12 changes: 6 additions & 6 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -700,9 +700,9 @@ CompleteValue(fieldType, fields, result, variableValues):
**Coercing Results**

The primary purpose of value completion is to ensure that the values returned by
field resolvers are valid according to the GraphQL type system and a service's
schema. This "dynamic type checking" allows GraphQL to provide consistent
guarantees about returned types atop any service's internal runtime.
_leaf type_ field resolvers are valid according to the GraphQL type system and a
service's schema. This "dynamic type checking" allows GraphQL to provide
consistent guarantees about returned types atop any service's internal runtime.

See the Scalars
[Result Coercion and Serialization](#sec-Scalars.Result-Coercion-and-Serialization)
Expand All @@ -723,9 +723,9 @@ and output of {CoerceResult()} must not be {null}.

**Resolving Abstract Types**

When completing a field with an abstract return type, that is an Interface or
Union return type, first the abstract type must be resolved to a relevant Object
type. This determination is made by the internal system using whatever means
When completing a field with an _abstract type_, that is an Interface or Union
return type, first the abstract type must be resolved to a relevant Object type.
Comment on lines +726 to +727
Copy link
Member

Choose a reason for hiding this comment

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

Alternative wording:

Suggested change
When completing a field with an _abstract type_, that is an Interface or Union
return type, first the abstract type must be resolved to a relevant Object type.
When completing a field whose return type is an _abstract type_ (an Interface or Union), first the abstract type must be resolved to a suitable Object type.

"a suitable object type" or "the relevant object type" I think?

This determination is made by the internal system using whatever means
appropriate.

Note: A common method of determining the Object type for an {objectValue} in
Expand Down