Skip to content

Conversation

AGalabov
Copy link
Collaborator

@AGalabov AGalabov commented Aug 7, 2025

I've managed to get z.lazy() working!

The code is not beautiful but it is also not the worst. Whilst doing it I also found this so I tried to add support for basic recursive schemas using getters. The code in this PR supported most of the functionality without any changes needed. However there are some edge cases that need handling. I managed to get them working but I definitely do not like how the code turned out for now so I separated it into another PR for a different set of comments.

@AGalabov AGalabov requested a review from georgyangelov August 7, 2025 07:19
@georgyangelov georgyangelov requested a review from Copilot August 7, 2025 15:40
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for Zod's z.lazy() functionality, enabling the generation of OpenAPI schemas for recursive and lazy-evaluated Zod schemas. The implementation includes support for both basic lazy schemas and recursive schemas using getters as described in Zod's documentation.

  • Adds a new LazyTransformer class to handle lazy schema transformation
  • Implements recursive schema detection and reference management using a "pending" state mechanism
  • Updates nullable type handling to prevent duplicate null types in type arrays

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/transformers/lazy.ts New transformer class for handling lazy schemas
src/transformers/index.ts Integrates lazy transformer into the main transformation pipeline
src/openapi-generator.ts Adds recursive schema handling with pending state management
src/v3.1/specifics.ts Improves nullable type handling to prevent duplicates
src/lib/zod-is-type.ts Adds ZodLazy type detection support
spec/types/lazy.spec.ts Comprehensive test suite for lazy schema functionality
spec/types/recursive-schemas.spec.ts Test suite for recursive schemas using getters
spec/lib/helpers.ts Adds validation to ensure no pending schemas remain

const defaultValue = Metadata.getDefaultValue(zodSchema);
const refId = Metadata.getRefId(zodSchema);

// TODO: Do I need a similar implementation as bellow inside constructReferencedOpenAPISchema
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word 'bellow' should be spelled 'below'.

Suggested change
// TODO: Do I need a similar implementation as bellow inside constructReferencedOpenAPISchema
// TODO: Do I need a similar implementation as below inside constructReferencedOpenAPISchema

Copilot uses AI. Check for mistakes.

};
}

return this.schemaRefs[refId];
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is duplicated logic for handling recursive schemas between this method and the LazyTransformer. Consider extracting this into a shared utility class or method to reduce code duplication and improve maintainability.

Suggested change
return this.schemaRefs[refId];
// Handle recursive schema references using shared utility
const recursiveSchema = this.getRecursiveSchemaRef(refId, isNullable);
if (recursiveSchema) {
return recursiveSchema;

Copilot uses AI. Check for mistakes.


if (refId && this.schemaRefs[refId] === undefined) {
this.schemaRefs[refId] = result;
if (refId && !this.schemaRefs[refId]) {
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition will be true when this.schemaRefs[refId] is 'pending', which could cause the schema to be regenerated instead of returning a reference. Consider checking explicitly for undefined: if (refId && this.schemaRefs[refId] === undefined) {

Suggested change
if (refId && !this.schemaRefs[refId]) {
if (refId && this.schemaRefs[refId] === undefined) {

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

@georgyangelov georgyangelov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach looks good, I haven't read every line but the | 'pending' sentinel value is how I would have done it.

const refId = Metadata.getRefId(zodSchema);

// TODO: Do I need a similar implementation as bellow inside constructReferencedOpenAPISchema
if (refId && typeof this.schemaRefs[refId] === 'object') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be more direct if we use !== 'pending', what do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do that it has to be this.schemaRefs[refId] && this.schemaRefs[refId] !== 'pending' as otherwise this.schemaRefs[refId] being undefined would also be considered "true"

// value within `generateSchemaWithRef`
if (refId && !this.schemaRefs[refId]) {
this.schemaRefs[refId] = 'pending';
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some duplication here

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

Successfully merging this pull request may close these issues.

2 participants