genqlient icon indicating copy to clipboard operation
genqlient copied to clipboard

Using interfaces to access generated types

Open opeik opened this issue 7 months ago • 1 comments

Hi there,

I'm trying to use interfaces to work around duplicate Go types being generated for the same GQL types when they're used in different queries. Something like this:

// genqlient code
package gql
type Query_Foo struct {
	Bar *string
        Baz *GetFoo_Foo_Baz
}

type GetFoo_Foo_Baz struct {
	Quux *int
}

func (v *Query_Foo) GetBar() *string {
	return v.Bar
}

func (v *Query_Foo) GetBaz() *Query_Foo_Baz {
	return v.Baz
}

func (v *GetFoo_Foo_Baz) GetQuux() *int {
	return v.Quux
}
// My code
package example
type gqlFoo interface {
    GetBar() *string
}

var a *gql.Query_Foo
x := func(gqlFoo) {}
x(a)

This works fine for accessing non-struct types, but when I try to do the same for Query_Foo.Baz, it breaks:

type gqlFoo interface {
    GetBar() *string
    GetBaz() *gqlBaz
}

type gqlBaz interface {
    GetQuux() *int
}


var a *gql.Query_Foo
x := func(gqlFoo) {}
x(a)
redacted/foo.go: cannot use a (variable of type *gql.Query_Foo) as example.gqlFoo value in argument to x:
     *gql.Query_Foo does not implement gqlFoo (wrong type for method GetBaz)
                have GetBaz() *Query_Foo_Baz
                want GetBaz() *gqlBaz

I feel like I'm missing something obvious... can I work around this?

Thanks in advance!

opeik avatar May 23 '25 03:05 opeik

Ah, I think I understand: the issue is that you're trying to use the "Go interfaces" style from the sharing types docs, but that doesn't work if you have several layers of structs.

We do have a few other approaches available here:

  • use GraphQL fragments (as described in the same docs -- this is probably the best approach in most cases)
  • set the typename explicitly (also the same docs)
  • use struct: true to avoid interfaces entirely (see here)

We could also generalize flatten to let you use it to avoid this issue, as described in #30.

I'm open to other solutions here as well, but I'm not sure if I see any offhand given Go interfaces don't support any kind of variance -- suggestions welcome! (It may be helpful to read the relevant section of the design doc as well as the one on why the names are complicated to understand some of the constraints.)

benjaminjkraft avatar Jun 28 '25 20:06 benjaminjkraft