Skip to content

allow unions to include interfaces and unions#950

Closed
yaacovCR wants to merge 4 commits intographql:mainfrom
yaacovCR:allow
Closed

allow unions to include interfaces and unions#950
yaacovCR wants to merge 4 commits intographql:mainfrom
yaacovCR:allow

Conversation

@yaacovCR
Copy link
Copy Markdown
Contributor

@yaacovCR yaacovCR commented May 27, 2022

Complements #939
Addresses #711

Similar to #939, this PR expands the robustness of the type system by allowing types that actually fulfill interfaces to be recognized as such by the GraphQL type system.

interface CloningInfo {
  ...
}

union CowOrWolf implements Animal = Cow | Wolf  # allowed by #939
union CowOrCloningInfo = Cow | CloningInfo  # allowed by this PR, note that CloningInfo is an interface
union WolfOrCloningInfo = Wolf | CloningInfo # allowed by this PR, note that CloningInfo is an interface

# note that here we are marking unions explicitly as included within a union.
# Adding a type to the CowOrWolf union will automatically add it to the ParentUnion
# We could also consider adding a constraint on the union definition, see below discussion
union Parent = CowOrCloningInfo | WolfOrCloningInfo | CowOrWolf | Cow | Wolf | CloningInfo

interface Animal {
  parent: Parent
}

type Cow implements Animal {
    parent: CowOrCloningInfo  # unlocked by this PR
}

type Wolf implements Animal{
    parent: WolfOrCloningInfo # unlocked by this PR
} 

Unions

With regard to unions, the goal is to explicitly mark some unions as members of other unions. We have two alternatives:

(A). Let unions include unions as members, as shown above. We could (or could not) require that all members of the unions also be listed (similar to how interfaces implementing child interfaces are required to explicitly list the parent.

Pro:

  1. Simple to reason about, fits with how unions currently work.
    Con:
  2. Could lead to some automatic behavior, when adding a type to a union that is part of a union, the type gets added to multiple unions. This is ameliorated by requiring all child union members to be explicitly listed.
  3. Union definitions could start to get pretty long if all the combinations must be listed therein.

(B) Add an additional optional constraint on the union requiring all of the members to be members of some other union, similar to how we have resolved #939.

Potential Syntax:

union CowOrWolf implements Animal, subtypes Parent = Cow | Wolf 
union CowOrCloningInfo subtypes Parent = Cow | CloningInfo 
union WolfOrCloningInfo subtypes Parent = Wolf | CloningInfo

union Parent = Cow | Wolf | CloningInfo
# cf
# union Parent = CowOrCloningInfo | WolfOrCloningInfo | CowOrWolf | Cow | Wolf | CloningInfo

Interfaces

For interfaces that are members of unions, it would not seem to make sense to require all the implementations of the interfaces to be listed independently. The whole point is that it is often just as useful to indicate that several interfaces might be returned as it is that several individual member types might be returned. For unions, we also have potentially multiple layers of nesting (unions of unions of unions) for which it would be extremely helpful to require the individual member types to be listed (or to use the second syntax above) while we don't have the same issue with interfaces.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 27, 2022

Deploy Preview for graphql-spec-draft ready!

Name Link
🔨 Latest commit d8e52f0
🔍 Latest deploy log https://app.netlify.com/sites/graphql-spec-draft/deploys/62e200650ec7d60008f9f9d5
😎 Deploy Preview https://deploy-preview-950--graphql-spec-draft.netlify.app/draft
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 27, 2022

Deploy Preview for graphql-spec-draft failed.

Name Link
🔨 Latest commit 18e70e5
🔍 Latest deploy log https://app.netlify.com/sites/graphql-spec-draft/deploys/62907cf3e15503000982fb5a

@leebyron leebyron added the 💡 Proposal (RFC 1) RFC Stage 1 (See CONTRIBUTING.md) label Jun 16, 2022
@yaacovCR
Copy link
Copy Markdown
Contributor Author

Now implemented in graphql/graphql-js#3682

I added requirement that all subtypes of included abstract types must be explicitly includes within the union. This can always be relaxed later.

with regard to requirement to list all transitive possible types, including member interfaces
@tobiasBora
Copy link
Copy Markdown

Any news on this? It has been around for 2 years now, and forbidding unions of unions create massive code duplication, possible errors, maintainability headaches, and poor readability. Consider for instance:

type QuestionMultipleChoice {
  question: String
  nbChoices: Int!
}

type QuestionText {
  question: String
}

type QuestionNumber {
  question: String
}

type WrongToken {
  _: Boolean
}

union Question = QuestionMultipleChoice | QuestionText | QuestionNumber

# What I'd like:
# union NewQuestionResult = Question | WrongToken
# What I need to write, **for each function that returns different errors**
union NewQuestionResult =  QuestionMultipleChoice | QuestionText | QuestionNumber| WrongToken

type Mutation {
  newQuestion: NewQuestionResult
}

type Query {
  hello: String
}

@yaacovCR
Copy link
Copy Markdown
Contributor Author

yaacovCR commented Mar 5, 2026

Closing for now, feel free to reopen if you would like to champion.

@yaacovCR yaacovCR closed this Mar 5, 2026
@tobiasBora
Copy link
Copy Markdown

What's the point of closing issues that make sense but just lack "champions"? Issue tracker are used to track what's left, and closing means that we will either never support or that it has been implemented, making it harder to search for missing features.

@martinbonnin
Copy link
Copy Markdown
Contributor

martinbonnin commented Mar 6, 2026

@tobiasBora as someone who's doing a lot of open source, I can empathize with OP here. Every issue I open adds a small mental load because I feel responsible for monitoring it and answering comments, etc...

I too have closed some issues that felt unactionable. In fact we recently started an issue club to make sure GraphQL issues stay actionable.

That being said, I agree that in that specific case, keeping the feature request would be useful. @yaacovCR would you be OK to reopen this as a "need champion" issue? Feel free to unsubscribe from notifications but this way someone else can take over and we keep the context and history?

@yaacovCR
Copy link
Copy Markdown
Contributor Author

yaacovCR commented Mar 6, 2026

Hi @tobiasBora !

First let me start off for apologizing for missing your earlier comment from over a year ago (!). I do try to respond to inquiries via Github repositories, but this one slipped through. I can be pinged via the GraphQL Discord if anyone would ever find that helpful with username yaacovCR.

In terms of your comment at that time

Any news on this?

This PR stalled out because after some initial progress, I ran it to some design complexity that was a bit difficult for me to overcome. I documented my understanding of the problem and some directions for solutions as an RFC: Expanding Subtyping, but I was not able to convince myself which approach forward was the best, and I reluctantly had to essentially step away as a champion for this change. That RFC and this PR live on and could form the basis of a proper solution if the issue finds a champion.

What's the point of closing issues that make sense but just lack "champions"? Issue tracker are used to track what's left

I would agree with this generally, although sometimes when the issue has garnered too much history, it does end up making sense to start fresh with a new issue, especially if there is a new angle. In this case, I'm not closing the issue / feature request, that remains open:

Rather, I'm closing the PR (that I opened) to address #711 because considering the time gap, I no longer believe that I will ever come back to it. It could be reopened if we find another champion who would like to work off of it, but I should have added that it would probably be best to reopen this as a new PR by the new champion.

Like you, I wish this would have worked out, and am a bit surprised at the complexity that this request unearthed. If you, like me, feel for whatever the reason unable to champion this feature request, a potentially easier step you could take would be to open a pull request on the RFC and add some argumentation as to which of the potential options for this feature should be chosen and why, or bring it up at a GraphQL WG meeting.

To sum up: the "closed" signal on this PR was not intended to "close out" the feature request, but to "close out" my attempt at addressing it, so that others are less confused as to "what happened." I apologize for not including more context as I closed the PR, which made the signal more confusing rather than less, and I apologize once again for failing to respond when you requested an update a year ago.

@martinbonnin I think #711 can be used to track the request, but you feel that it's helpful to have spec PRs live on in the open state, I won't object to its re-opening. I think, though, that it can still be useful as a reference in the "closed" state, and as I expect any future champion to make their own PR, I only expect to revive this one if I find a forward direction that in which I feel more confident.

@martinbonnin
Copy link
Copy Markdown
Contributor

Thanks for the follow up and context @yaacovCR ❤️ Tracking the feature request in #711 makes a lot of sense to me.

@tobiasBora
Copy link
Copy Markdown

tobiasBora commented Mar 6, 2026

Oh my bad, I thought you were closing the issue, not the PR, I read it too quickly, excuse me! And thanks for the context it also helps a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💡 Proposal (RFC 1) RFC Stage 1 (See CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants