-
Notifications
You must be signed in to change notification settings - Fork 87
[DRAFT] Feature/#247 zod lazy #335
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
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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 |
There was a problem hiding this comment.
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'.
// 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]; |
There was a problem hiding this comment.
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.
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]) { |
There was a problem hiding this comment.
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) {
if (refId && !this.schemaRefs[refId]) { | |
if (refId && this.schemaRefs[refId] === undefined) { |
Copilot uses AI. Check for mistakes.
There was a problem hiding this 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') { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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'; | ||
} |
There was a problem hiding this comment.
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
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.