apollo-tooling icon indicating copy to clipboard operation
apollo-tooling copied to clipboard

code generator generating strange typescript union types

Open smoak opened this issue 3 years ago • 6 comments

Intended outcome:

Generating types from a query should not include an "empty" type and union it with the actual requested type.

Actual outcome:

apollo client:codegen is generating an empty typescript object type. It is then unioned with the requested type from the query.

generated code:

export interface PopularTopicsClients_group_Topic {} // here is the strange "empty" type

export interface PopularTopicsClients_group_Group_featuredTopics_edges_node {
  id: string;
}

export interface PopularTopicsClients_group_Group_featuredTopics_edges {
  node: PopularTopicsClients_group_Group_featuredTopics_edges_node;
  threadCount: number;
}

export interface PopularTopicsClients_group_Group_featuredTopics {
  edges: (PopularTopicsClients_group_Group_featuredTopics_edges | null)[];
}

export interface PopularTopicsClients_group_Group {
  featuredTopics: PopularTopicsClients_group_Group_featuredTopics;
}

// This unioned type is strange because `PopularTopicsClients_group_Topic` has no fields 
// and is not being requested in the query
export type PopularTopicsClients_group = PopularTopicsClients_group_Topic | PopularTopicsClients_group_Group;

export interface PopularTopicsClients {
  group: PopularTopicsClients_group | null;
}

export interface PopularTopicsClientsVariables {
  first?: number | null;
  groupId: string;
}

The strange type is export type PopularTopicsClients_group = PopularTopicsClients_group_Topic | PopularTopicsClients_group_Group;

PopularTopicsClients_group_Topic is a type with no fields and it's not being requested in the query (see the repro below). This makes it challenging to use in code because group is either the fields ive asked for PopularTopicsClients_group_Group or some empty type. However, this does not match what the server will be returning, nor the structure of the query so it's quite confusing. So using it in code requires a guard:

if (isEmptyType(group)) {
throw new Error('this is just so typescript is happy...');
}
// now group is definitely `PopularTopicsClients_group_Group` so we can access the fields on it
// do stuff with `group.featuredTopics`

How to reproduce the issue:

Use the following schema.graphql:

interface Node {
    id: ID!
}

type Group implements Node {
  id: ID!

  featuredTopics(
    first: Int
    ): GroupTopicConnection!
}

type Topic implements Node {
  id: ID!
}

type GroupTopicConnection {
    edges:[GroupTopicEdge]!

    pageInfo: PageInfo!
}

type GroupTopicEdge {
    node: Topic!

    cursor: String!

    threadCount: Int!
}

type PageInfo {
    hasNextPage: Boolean!

    hasPreviousPage: Boolean!

    startCursor: String

    endCursor: String
}

type Query {

    node(
        id: ID!
    ): Node
  
}

With the following query:


query PopularTopicsClients($first: Int = 5, $groupId: ID!) {
  group: node(id: $groupId) {
    ... on Group {
      featuredTopics(first: $first) {
        edges {
          node {
            id
          }
          threadCount
        }
      }
    }
  }
}

Using the following command:

$ npx apollo client:codegen --includes=queryPopularTopics.graphql --target typescript --no-addTypename  --localSchemaFile="schema.graphql" --outputFlat src/types --customScalarsPrefix GraphQl --globalTypesFile="src/types/globalTypes.ts"

Versions Tried it on:

apollo/2.30.2 linux-x64 node-v10.22.0

smoak avatar Aug 21 '20 00:08 smoak

What happens when you remove --no-addTypename?

kaleb avatar Sep 03 '20 00:09 kaleb

I have a similar issue. I believe it has something to do with the fact that you have the Node type on your RootQueryType that other types inherit from. If you included the __typename you will probably get a generated PopularTopicsClients_group_Topic type with the __typename value being a union type of all the the types that implement node minus the Group type as it is already generated into one of the possible node types. I am not sure what the solution is however. Did you find a way to resolve this?

div-dog avatar Feb 25 '21 03:02 div-dog

I am also interested in finding the solution to this. I would have assumed that the types would only be generated for the requested fragment. so ...on Group { should only have types generated for Group and not the other types on the node interface? Is the answer just to manually check the __typename?

bentron2000 avatar Mar 16 '21 00:03 bentron2000

Is the answer just to manually check the __typename?

That is what I ended up doing. Couldn't find a better way :(

smoak avatar Mar 30 '21 21:03 smoak

any updates for this?

roman-hula avatar Feb 04 '22 14:02 roman-hula

Hello. Do you guys have a work-around for this?

calvo-jp avatar Apr 16 '22 12:04 calvo-jp