Skip to content
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

typescript-resolvers v4.2.1 causes error when typechecking: Maximum call stack size exceeded #10084

Closed
dylanwulf opened this issue Aug 6, 2024 · 17 comments
Assignees

Comments

@dylanwulf
Copy link

dylanwulf commented Aug 6, 2024

Which packages are impacted by your issue?

@graphql-codegen/typescript-resolvers

Describe the bug

With the v4.2.1 release of @graphql-codegen/typescript-resolvers, I started seeing an error Maximum call stack size exceeded when running the typescript compiler. This did not happen on v4.2.0.

Your Example Website or App

https://github.com/dylanwulf/graphql-code-generator-issue-sandbox-template/tree/typescript-resolvers-max-call-stack-exceeded

Steps to Reproduce the Bug or Issue

  1. in the linked github repo, check out branch typescript-resolvers-max-call-stack-exceeded
  2. npm install
  3. npm run codegen (optional -- the types have already been generated and committed)
  4. npm run typecheck
  5. see error

Expected behavior

I expected no error

Screenshots or Videos

No response

Platform

  • OS: Windows 11
  • NodeJS: 20.13.1
  • graphql version: see package.json in linked github repo
  • @graphql-codegen/* version(s): see package.json in linked github repo

Codegen Config File

See linked github repo

Additional context

The full error output I see in the console:

> graphql-codegen-issue-template@1.0.0 typecheck
> tsc

C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:98439
                throw e;
                ^

RangeError: Maximum call stack size exceeded
    at Object.createBaseTokenNode (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:22176:39)
    at createBaseToken (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:18429:32)
    at createBaseLiteral (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:18308:24)
    at createBaseStringLiteral (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:18326:24)
    at Object.createStringLiteral (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:18331:24)
    at typeToTypeNodeWorker (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:43717:88)
    at typeToTypeNodeHelper (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:43641:32)
    at mapToTypeNodes (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:44401:40)
    at typeToTypeNodeWorker (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:43831:37)
    at typeToTypeNodeHelper (C:\Users\redacted\projects\graphql-code-generator-issue-sandbox-template\node_modules\typescript\lib\tsc.js:43641:32)

Node.js v20.13.1
@eddeee888
Copy link
Collaborator

Hi @dylanwulf ,

I found that the problem seems to stem from the custom DeepPartial. When I remove it from the codegen config, it works fine.

Could you please let me know your use case with DeepPartial? This seems to be fairly commonly used in some setup but I haven't found a use case for it.

@dylanwulf
Copy link
Author

Hi @dylanwulf ,

I found that the problem seems to stem from the custom DeepPartial. When I remove it from the codegen config, it works fine.

Could you please let me know your use case with DeepPartial? This seems to be fairly commonly used in some setup but I haven't found a use case for it.

@eddeee888 We use @graphql-tools/mock when providing mock graphql data to our frontend unit tests. @graphql-tools/mock allows us to specify deeply partial data, and it will automatically generate any missing data in a way that conforms to our graphql schema. DeepPartial allows us to have type safety when providing this deeply partial data.

@eddeee888
Copy link
Collaborator

I see @dylanwulf . Could you please show some code snippet how you'd set up @graphql-tools/mock? I'm looking at the doc but can't imagine how codegen type's used with it (This is the first time I'm looking into it)

Regarding, the Maximum call stack size exceeded issue, I'm trying to think what's the best way to handle it. But I'm guessing DeepPartial loops through nested types which causes the infinite loop issue at typecheck.

@kyle-villeneuve
Copy link

kyle-villeneuve commented Sep 1, 2024

Something is reallly different about typescript-resolvers 4.2.1, the types that were generated for my project were so inefficient that I couldn't even get typescript to build my project, I generated a trace and it ended up being 34GB, the build info wouldn't even write to a file because the size was > JSON.stringify allowed. vscode was also complaining about infinite types and my editor was totally janked as well. Downgraded to 4.1.0 and the problems disappeared. Not saying that I didn't somehow write my types in a way that was causing everything to blow up but I wasted two days trying to fix this and realized 4.2 was the issue. I was also using a defaultMapper and this may have something to do with that feature and the codegen output differences seemed to mostly be related to union types.

@eddeee888
Copy link
Collaborator

Hi @kyle-villeneuve , could you please let me know the value of defaultMapper ?

@kyle-villeneuve
Copy link

// codegen.config.ts

defaultMapper: 'DeepPartialGQL<{T}>'

// type definition

export declare const __brand: unique symbol;
export type OpaqueType<T, K extends string = 'BRANDED_TYPE'> = T & { readonly [__brand]?: K };
type ValueOf<T> = T[keyof T];

export interface DeepPartialArray<T> extends Array<DeepPartialGQL<T>> {}

export declare type DeepPartialObject<T> = {
  [P in keyof T]?: DeepPartialGQL<T[P]>;
} & { __typename?: string };

export declare type DeepPartialGQL<T> = T extends Function
  ? T
  : T extends Array<infer U>
    ? DeepPartialArray<U>
    : T extends ValueOf<Opaque>
      ? T | undefined | null
      : T extends object
        ? DeepPartialObject<T>
        : T | undefined | null;

@eddeee888
Copy link
Collaborator

May I know the reason you are using DeepPartialGQL in your config @kyle-villeneuve ? 🙂

@kyle-villeneuve
Copy link

@eddeee888
Yeah, this is for server side code, and my resolvers don't always return the full type, sometimes there is additional fetching in the "leaves" subtypes so I allow returning partial responses while still ensuring type safety to some degree

@eddeee888
Copy link
Collaborator

Thanks @kyle-villeneuve , have you tried using mappers?
This option is designed to allow you to return a consistent value in resolvers, so the next resolvers in the chain can use said returned type. It's the better way to enforce type-safety and consistency IMO 🙂

@kyle-villeneuve
Copy link

@eddeee888 I did yeah but that ended up being too restrictive for my use case

@eddeee888
Copy link
Collaborator

I'll see what I can do 🙂
Just a note, mappers will have first-class support so new features will be tested against it. I think defaultMapper was added to support some use cases but may not have the guarantee to always work.

@kgregory
Copy link

I'll see what I can do 🙂 Just a note, mappers will have first-class support so new features will be tested against it. I think defaultMapper was added to support some use cases but may not have the guarantee to always work.

This is a bit confusing because both features are documented with the same level of importance and there's no indication provided other than this comment that users should avoid defaultMapper. In fact, this exact use of DeepPartial as the defaultMapper is an example from the docs.

@eddeee888
Copy link
Collaborator

You are right, let me have a chat with the team to understand this a bit more.

@eddeee888
Copy link
Collaborator

Hi all,
There will be an option called avoidCheckingAbstractTypesRecursively. Please use it together with the defaultMapper option if defaultMapper is recursive:

config: {
  defaultMapper: 'DeepPartial<{T}>',  
  avoidCheckingAbstractTypesRecursively: true // required if you have complex nested abstract types
},

There's an alpha version here: 4.4.0-alpha-20241014102200-ddc58bcd59abae2c907ea72c013c42ae94232b54
Please try it out and let me know if it fixes the issue. Once confirmed, I can try to release it ASAP.

@dylanwulf
Copy link
Author

dylanwulf commented Oct 16, 2024

@eddeee888 Thank you! That alpha version with the new option fixes the issue for me!

@eddeee888
Copy link
Collaborator

This is fixed in @graphql-codegen/typescript-resolvers@4.4.0. Thank you for reporting!

@kyle-villeneuve
Copy link

thank you Eddeee!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants