-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Interface confusion #338
Comments
Hm, sounds tricky. It's tough to advise here without seeing more of the code; can you post an example where you get those two error types?
Yeah, that's the specification for interfaces rather than documentation on how to use them. You're right in that our documentation for interfaces is mostly by example right now.
Note that #327 was a breaking change here on how interfaces are added to a schema; the test changes in there should help clarify the usage now. Note that
Hm, I'm not sure why you would want to instantiate an interfaces type; can you go into more detail?
Yeah, you shouldn't need to do so; when defining an object type, you can pass a thunk to the Hope this helps! |
Thanks @dschafer. Let me expound a bit. Here's the import {
GraphQLEnumType,
GraphQLNonNull,
GraphQLString,
GraphQLInterfaceType } from 'graphql';
import iconType from './icon';
import logoType from './logo';
const graphicFileFormatsEnum = new GraphQLEnumType({
name: 'graphicFileFormats',
description: 'The various file formats in which graphics can be delivered.',
values: {
SVG: {
value: 'svg',
description: 'Scalable Vector Graphics format',
},
PNG: {
value: 'png',
description: 'Portable Network Graphics format',
},
},
});
const graphicType = new GraphQLInterfaceType({
name: 'graphic',
description: 'A visual graphic used in product UIs.',
fields: () => ({
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'The name of the graphic',
},
format: {
type: graphicFileFormatsEnum,
description: 'The various file formats a graphic can be delivered in.',
},
resolveType: (value) => {
if (value instanceof logoType) {
return logoType;
}
else if (value instanceof iconType) {
return iconType;
}
else {
return null;
}
},
}),
});
export { graphicFileFormatsEnum };
export default graphicType; And 2 implementing types: import {
GraphQLNonNull,
GraphQLString,
GraphQLObjectType } from 'graphql';
import graphicType, { graphicFileFormatsEnum } from './graphic';
const logoType = new GraphQLObjectType({
name: 'Logo',
description: 'An image representing an offering.',
interfaces: graphicType,
fields: () => ({
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'The name of the logo.',
},
format: {
type: graphicFileFormatsEnum,
description: 'The various file formats a graphic can be delivered in.',
},
}),
isTypeOf: (value) => value instanceof logoType,
});
export default logoType; import {
GraphQLEnumType,
GraphQLNonNull,
GraphQLString,
GraphQLObjectType } from 'graphql';
import graphicType, { graphicFileFormatsEnum } from './graphic';
const iconCollectionNamesEnum = new GraphQLEnumType({
name: 'iconCollections',
description: 'The various types of collections that icons belong to.',
values: {
ACTION_BASED: {
value: 'action-based',
description: 'Icons used in action-based UI components',
},
OBJECT_BASED: {
value: 'object-based',
description: 'Icons representing common objects in UIs.',
},
FORMATTING: {
value: 'formatting',
description: 'Icons used for content creation and interactivity',
},
},
});
const iconType = new GraphQLObjectType({
name: 'Icon',
description: 'A visual graphic asset for UI components.',
interfaces: graphicType,
fields: () => ({
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'The name of the icon found in the IBM Design Language.',
},
collection: {
type: iconCollectionNamesEnum,
description: 'The name of the collection the icon belongs to.',
},
format: {
type: graphicFileFormatsEnum,
description: 'The various file formats a graphic can be delivered in.',
},
}),
isTypeOf: (value) => value instanceof iconType,
});
export { iconCollectionNamesEnum };
export default iconType; Hope that helps. |
I see. So if you implement Looking at the implementation of isTypeOf: (value) => value instanceof iconType, When I think about GraphQL schemas and types, I generally think about them at two different layers: there's a business object, and there's the type that wraps it. The object that gets passed into the So if you have a field that returns a someGraphic: {
type: new GraphQLNonNull(Graphic),
resolve: (obj) => getSomeGraphic()
}, Then the question that we're trying to answer in So I'm not sure |
Thanks, so, removing the As a result, these are failed tests I'm seeing in
|
Is there more info I can give to help wade through this confusion? |
It seems like there are two issues here: The first is that
This seems like something we should highlight with an early error. If that doesn't fix the issue, a second problem may be: import iconType from './icon';
import logoType from './logo'; in your interface file. That's creating the circular dependency and causing those files to be included before the interface is defined. Another way to solve this might be declaring your interfaces as a thunk, so they're not expected to be a live reference until the full schema is constructed:
|
Thanks @leebyron. Unfortunately the thunk format is the only suggestion I haven't tried, and it changes nothing. I think I'm missing something more fundamental that's implied by existing implementations or docs.
|
That error implies that whatever is provided to |
So here's the weird thing. Graphic is not undefined. I just logged I still had the The odd thing is, queries for those 2 child types still resolve to When I run |
@kevinSuttle any luck on this? |
Honestly, it's a side project, so I had to put it aside for a bit. I need to re-read this post by @clayallsopp again. I think I may be looking for a Union type. https://medium.com/the-graphqlhub/graphql-tour-interfaces-and-unions-7dd5be35de0d#.uw6ndo5hl |
Declaring interfaces as a thunk as described by @leebyron earlier in the thread resolved the following issue for me: What I had:: What I needed: For me, this issue started occurring when I moved my node interface definitions into their own file. Thanks @kevinSuttle for opening the ticket and thanks to @leebyron for your diligence (and solution!) |
The thunk solves this for me also, the examples should be updated to highlight issues with circular dependencies. |
This may be resolved now, but looking at the code examples above the value being set to 'interfaces' in the Object Config is a graphql type and it should be an array of types. E.g. 'Interfaces: [graphicType]' |
Since every graphql type has a unique string name, could this be used to opt-in to an interface or for |
I don't see any actionable items for
@WickyNilliams Thanks for suggestions. You can return names from |
carried over from graphql/graphql-relay-js#25 (comment)
x.resolveType field type must be Output Type but got: undefined.
Error: X may only implement Interface types, it cannot implement: undefined.
I've run into one of these or a circular dependency a number of times following the docs and examples in the wild.
It seems developers always end up needing to implement a type registry, which I thought a Schema was.
https://facebook.github.io/graphql/#sec-Interface-type-validation The docs here are almost empty, but even the one caveat makes very little sense. The section above it doesn't even mention
isTypeOf, valueOf,
orresolveType
. I've found these on GitHub or StackOverflow.Using the
_schema
introspection, Interfaces themselves don't appear as types (at least in my experience), which seems confusing, as does the following result:Another example: An app I'm working on will behave very differently depending on if I wrap a single Interface type with Array brackets as well.
interfaces: graphicType
vsinterfaces: [graphicType]
If do the latter, I get
Error: Box may only implement Interface types, it cannot implement: undefined.
If I do the former, all the types that implement that interface return
null
, and get this in GraphiQL:Another confusing part of the spec is that interfaces are just fields. e.g. neither of the following work.
const boxType = new GraphicType({
const boxType = graphicType({
Babel throws an error.
Instead, you need to make it a predefined type.
Can you see how confusing that is, especially coming from an OOP perspective?
One suggestion from reactiflux discord was to put the Interface and its implementing types into one file, but that smells funny to me.
The text was updated successfully, but these errors were encountered: