graphql-code-generator: Generated typescript recursive query types are wrong

Describe the bug

Because codegen relies heavily on Intersection types, typescript does not correctly check query fields which are optional, and allows nulls to go unchecked in code.

An example query:

type Query {
    categories(id: ID!): [Category!]!
}

type Category {
    id: ID!
    name: String!
    children: [Category!]
}
query GetCategories {
    categories(id: 1) {
        id
        name
        children {
            id
            name
            children {
                id
                name
            }
        }
    }
}

To Reproduce

Use the code sandbox:

https://codesandbox.io/s/graphql-codegen-issue-template-563vy

yarn generate will generate the query types for the nested query on Category

test.ts shows how these types provide no protection for null fields in typescript.

We generate some query response data which is a valid structure for the GetCategoriesQuery and then use that data in a function which uses the Category type as if it’s children are not null (should throw an error)

yarn build will show how no type errors are generated from the types.

yarn test will show how a runtime error exists when the .length property is queried on the children field.

Expected result

GetCategoriesQuery should not be compatible with Category as children is not expected to be a null type in Category.

Environment:

  • OS: MacOS
  • @graphql-codegen/...@1.7.0:
  • NodeJS: 10

Additional context

Typescript has issues with intersections being tracked https://github.com/microsoft/TypeScript/issues/33243. Using them for generating these types seems to be the issue here, as manually generating the type without intersections correctly picks up the errors.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 26 (2 by maintainers)

Most upvoted comments

@freshollie Maybe I’m missing something, not sure.

In your use case, you are using:

children: [Category!]

Which means that children field could be null, but the array values must be set. If you wish to have a nullable type, you need to use children: [Category!]!.

With your schema, it seems to work for me in TS Playground:

image

And if I’m changing it to force a value there and make the field as non-nullable, it allow me to access it directly:

image

You shouldn’t use Category type (generated by typescript) directly - it’s a base type that contains all fields of the type, and then queries types can just Pick from it. GraphQL let you choose the fields and it means that always there is a base type and a selection set based on that type.

If you wish to have a dedicated type for Category of your query, use GraphQL fragments and then you’ll get a type per each fragment type and it will represent the actual representation of Category based on your fragment selection set.

@freshollie Thanks, closing for now. The current default is using Pick but I think maybe we can change it in a future version, we just need to make sure preResolveTypes is stable and has no issues.

@dotansimha @n1ru4l any conclusions on this? To summaries: preResolveTypes: true fixes our issue, so I am fine to close.

Other people may encounter this and preResolveTypes: true is not enabled by default, so documenting this behaviour might be the solution?