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

Zod: Recursive schema #769

Closed
vferak opened this issue Jan 12, 2024 · 9 comments · Fixed by #780
Closed

Zod: Recursive schema #769

vferak opened this issue Jan 12, 2024 · 9 comments · Fixed by #780
Assignees
Labels
bug Something isn't working

Comments

@vferak
Copy link

vferak commented Jan 12, 2024

What version of kubb is running?

10.1.0

What platform is your computer?

Linux

What version of external packages are you using(@tanstack-query, MSW, React, Vue, ...)

"zod": "^3.22.2"

What steps can reproduce the bug?

When generating zod schemas from attached OpenAPI specification, it generates unusable zod schema, because it contains self reference in its inicialization.

Result of itemSchema.ts generated from attached file.

export const itemSchema = z.object({
    name: z.string(),
    children: z.array(z.lazy(() => itemSchema).schema) // <-- self reference to itemSchema
});

The typescript type is generated correctly.

export type Item = {
    /**
     * @type string
     */
    name: string;
    /**
     * @type array
     */
    children: Item[];
};

How often does this bug happen?

Every time

What is the expected behavior?

Kubb can generate recursive Zod schemas (example of implementation).

Swagger/OpenAPI file?

Whole specification in file here: openapi.json

Troublesome part:

"components": {
    "schemas": {
      "Item": {
        "type": "object",
        "required": ["name", "children"],
        "properties": {
          "name": {
            "type": "string"
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Item"
            }
          }
        }
      }
    }
  }

Additional information

No response

@vferak vferak added the bug Something isn't working label Jan 12, 2024
@stijnvanhulle stijnvanhulle self-assigned this Jan 14, 2024
@stijnvanhulle
Copy link
Collaborator

This was fixed with kubb version 2.1.0, see #744

@vferak
Copy link
Author

vferak commented Jan 15, 2024

Thank you for your answer!

After updating to version 2.3.0, the code now works. 👍

However, Typescript is still complaining. 😕

After compilation it throws this error:
'itemSchema' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

export const itemSchema = z.object({  // <-- ts-compile complains here
    name: z.string(),
    children: z.array(z.lazy(() => itemSchema)) // <-- self reference to itemSchema
});

Yes, i can ignore the whole generated folder in tsconfig, but i would like it to work.

I'm able to fix this error by adding type annotation to the schema like this:

export const itemSchema: z.ZodType<Item> = z.object({  // <-- everything is fine now
    name: z.string(),
    children: z.array(z.lazy(() => itemSchema))
});

Would something like this be possible?

I know it adds another plugin dependecy to zod plugin (typescript plugin), but i think it would be worth it.

@stijnvanhulle
Copy link
Collaborator

@vferak This seems more like a TypeScript config issue, maybe this related Stackoverflow issue can help: https://stackoverflow.com/questions/41944650/this-implicitly-has-type-any-because-it-does-not-have-a-type-annotation.

@vferak
Copy link
Author

vferak commented Jan 15, 2024

The issue you linked is concerned mainly about the this keyword, which is not my issue.

In tsconfig i can only ignore this file or enable any in my codebase with noImplicitAny (neither of which i want).

The issue really stems from the fact, that Typescript can't infer the type of itemSchema, because it contains the self reference. It's clearly described in zod documentation https://github.com/colinhacks/zod?tab=readme-ov-file#recursive-types.

You can define a recursive schema in Zod, but because of a limitation of TypeScript, their type can't be statically inferred. Instead you'll need to define the type definition manually, and provide it to Zod as a "type hint".

So as far as i can tell, adding the typehint seems like the only solution for now.

@stijnvanhulle stijnvanhulle reopened this Jan 15, 2024
@stijnvanhulle stijnvanhulle linked a pull request Jan 18, 2024 that will close this issue
@vferak
Copy link
Author

vferak commented Jan 22, 2024

Can confirm, that after upgrading to version v2.5.0 and configuring Zod with option { typed: true } everything works as expected! 🥳

Detailed config solving this issue is here.

Thank you very much @stijnvanhulle. 🔥

@austinlangdon
Copy link

austinlangdon commented May 13, 2024

@vferak what package did you need to upgrade to resolve the issue? v2.50 of what?

I'm also experiencing this issue running the latest of kubb

cc: @stijnvanhulle

@stijnvanhulle
Copy link
Collaborator

import { defineConfig } from '@kubb/core'
import { pluginOas } from '@kubb/plugin-oas'
import { pluginZod } from '@kubb/swagger-zod'

export default defineConfig({
  input: {
    path: './petStore.yaml',
  },
  output: {
    path: './src/gen',
  },
  plugins: [
    pluginOas(),
    pluginZod({
      output: {
        path: './zod',
      },
      typed: true // this will force the typings instead of relying on infer of TS(which can cause issues)
    }),
  ],
})

@austinlangdon
Copy link

Thank you @stijnvanhulle I added typed: true to the config, but the error still persisting.

Only thing that helping is removing .schema reference

Changing this:

z.lazy(() => metadataValueSchema.schema)

to

z.lazy(() => metadataValueSchema)

I noticed #744 (fix: removal of .schema) did this as well

Why would .schema still be appended within lazy()?

@vferak
Copy link
Author

vferak commented May 24, 2024

@austinlangdon yes, the .schema reappeared in our schemas, which also broked our generated code 😕.

Hovewer, after upgrading to Kubb v2.19.1, they are once again gone 😀.

So i recommend upgrading to fix this issue 🙂.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants