Summary
When RelaxSubgraphOperationFieldSelectionMergingNullability is enabled, field selection merging should allow nullability differences between types that cannot overlap at runtime. The potentiallySameObject function currently treats any interface+object pair as potentially overlapping, which prevents nullability relaxation even when the object type does not implement the interface.
Example
Using the existing boxDefinition test schema, consider these types:
NonNullStringBox1 — an interface with scalar: String!
StringBox — an object type that implements SomeBox but does not implement NonNullStringBox1, with scalar: String
The following query:
{
someBox {
... on NonNullStringBox1 { scalar }
... on StringBox { scalar }
}
}
produces this validation error even with RelaxSubgraphOperationFieldSelectionMergingNullability enabled:
fields 'scalar' conflict because they return conflicting types 'String!' and 'String'
This is incorrect. Since StringBox does not implement NonNullStringBox1, no runtime object can ever satisfy both type conditions simultaneously. The types are guaranteed to be non-overlapping, so the nullability difference is safe to relax.
Root cause
In potentiallySameObject, the interface case is a single branch:
case left.Kind == ast.NodeKindInterfaceTypeDefinition || right.Kind == ast.NodeKindInterfaceTypeDefinition:
return true
This returns true (potentially overlapping) for any pair where at least one side is an interface, regardless of whether the other type actually implements that interface.
Proposed fix
Split the single interface case into three:
- both interfaces → conservatively assume overlap (
true)
- interface + object → overlap only if the object type implements the interface (check via
NodeImplementsInterfaceNode)
- two objects → overlap only if same type name
I have a PR ready for this: #1454
Summary
When
RelaxSubgraphOperationFieldSelectionMergingNullabilityis enabled, field selection merging should allow nullability differences between types that cannot overlap at runtime. ThepotentiallySameObjectfunction currently treats any interface+object pair as potentially overlapping, which prevents nullability relaxation even when the object type does not implement the interface.Example
Using the existing
boxDefinitiontest schema, consider these types:NonNullStringBox1— an interface withscalar: String!StringBox— an object type that implementsSomeBoxbut does not implementNonNullStringBox1, withscalar: StringThe following query:
{ someBox { ... on NonNullStringBox1 { scalar } ... on StringBox { scalar } } }produces this validation error even with
RelaxSubgraphOperationFieldSelectionMergingNullabilityenabled:This is incorrect. Since
StringBoxdoes not implementNonNullStringBox1, no runtime object can ever satisfy both type conditions simultaneously. The types are guaranteed to be non-overlapping, so the nullability difference is safe to relax.Root cause
In
potentiallySameObject, the interface case is a single branch:This returns
true(potentially overlapping) for any pair where at least one side is an interface, regardless of whether the other type actually implements that interface.Proposed fix
Split the single interface case into three:
true)NodeImplementsInterfaceNode)I have a PR ready for this: #1454