Graphql-code-generator: __typename in query for interface isn't a union when named fragments are used

Created on 5 Jan 2019  路  3Comments  路  Source: dotansimha/graphql-code-generator

Sorry the title is a bit of a mouthful. :)

Describe the bug

When generating types for a query for an interface, the __typename field is set to just the interface name, which means you can't switch on its implementors. This only happens when the subtypes are in named fragments, it works fine with inline fragments.

To Reproduce

Schema:

type Query {
  task: Task
}

interface Task {
  id: ID!
}

type A implements Task {
  id: ID!
  a: String
}

type B implements Task {
  id: ID!
  b: String
}

Query:

query {
  task {
    id
    __typename

    ...AFrag
    ...BFrag
  }
}

fragment AFrag on Task {
  ... on A {
    a
  }
}

fragment BFrag on Task {
  ... on B {
    b
  }
}

Generated types (line 18):
screen shot 2019-01-05 at 4 19 08 pm

Note: I would've copied the generated code in here, but the playground on your site doesn't allow copy and paste :(

Expected behavior

I expect __typename on Task to be "A" | "B".

Environment:

  • Codegen: 0.15.2

Additional context

Compare the above behaviour to the behaviour of inline fragments...

Schema:

type Query {
  task: Task
}

interface Task {
  id: ID!
}

type A implements Task {
  id: ID!
  a: String
}

type B implements Task {
  id: ID!
  b: String
}

Query:

query {
  task {
    id
    __typename

    ...AFrag
    ...BFrag
  }
}

Generated types:
screen shot 2019-01-05 at 4 23 15 pm

And you can see the __typename of Task is correctly the union of the fragment's __typename.

bug plugins waiting-for-release

Most helpful comment

Hi.
I have the same issue.
But imo the __typename is more difficult than that.

In my case, backend developpers added a new Task implementation.

type C implements Task {
  id: ID!
  c: String
}

But all the implementation that was done in the front were

if (obj.__typename === 'A') {
   // Do something for type A
} else {
  // Do something for type B
}

which is actually a mistake because now all type C goes into the else.

IMO, the __typename in these kind of case should be 'Task' | 'A' | 'B' which is the safest way, you can add as much implementation as you want and the front code is still going to be correct. 'Task' is here representing all the futur implementation that you can get.

Hope I'm clear.
Regards

All 3 comments

Hi.
I have the same issue.
But imo the __typename is more difficult than that.

In my case, backend developpers added a new Task implementation.

type C implements Task {
  id: ID!
  c: String
}

But all the implementation that was done in the front were

if (obj.__typename === 'A') {
   // Do something for type A
} else {
  // Do something for type B
}

which is actually a mistake because now all type C goes into the else.

IMO, the __typename in these kind of case should be 'Task' | 'A' | 'B' which is the safest way, you can add as much implementation as you want and the front code is still going to be correct. 'Task' is here representing all the futur implementation that you can get.

Hope I'm clear.
Regards

Fixed in the recent refactor. we now override __typename and we are using brackets with | when fragments for different types are used, it also means that you can get better fragments matching and the correct __typename.

Fix available in 1.0.0 馃憤

Was this page helpful?
0 / 5 - 0 ratings