-
- Notifications
You must be signed in to change notification settings - Fork 1.7k
Add .toJSONSchema() method #5477
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
Conversation
| cc @claude review this |
| Claude finished @colinhacks's task —— View job Code Review: |
This reverts commit 3e8ba43.
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 a .toJSONSchema() method to Zod schemas and refactors the JSON Schema generation from a class-based to a functional approach. The changes include:
- Refactored
JSONSchemaGeneratorclass to use functional approach with processor pattern - Added
.toJSONSchema()instance method to all Zod schema types - Added support for StandardJSONSchemaV1 interface with
~standard.jsonSchemaproperties - Created separate processor functions for each schema type in new
json-schema-processors.tsfile
Key Changes
- Separated JSON Schema generation logic into modular processor functions
- Normalized target versions to use hyphenated format (
"draft-04","draft-07") - Made legacy
JSONSchemaGeneratorclass a wrapper around new functional implementation - Added comprehensive test coverage for new
.toJSONSchema()methods
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
packages/zod/src/v4/core/json-schema-processors.ts | New file containing processor functions for each schema type |
packages/zod/src/v4/core/to-json-schema.ts | Refactored to functional approach, added support for StandardJSONSchema |
packages/zod/src/v4/core/to-json-schema-old.ts | Copy of original implementation for reference |
packages/zod/src/v4/core/standard-schema.ts | Added StandardJSONSchemaV1 and StandardTypedV1 interfaces |
packages/zod/src/v4/core/schemas.ts | Added processJSONSchema hook to schema internals |
packages/zod/src/v4/classic/schemas.ts | Added toJSONSchema method and ~standard.jsonSchema to all schema types |
packages/zod/src/v4/classic/tests/to-json-schema-methods.test.ts | New comprehensive tests for .toJSONSchema() methods |
packages/zod/src/v4/classic/tests/to-json-schema.test.ts | Updated import path from zod/v4 to zod |
packages/zod/src/v4/classic/tests/standard-schema.test.ts | Added tests for StandardJSONSchemaV1 conformance |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| cc @pullfrog review |
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.
Strong refactoring that improves architecture. Found 2 critical bugs with draft-04 handling, import organization issues, and opportunities for better documentation.

| } | ||
| if (typeof minimum === "number") { | ||
| json.minimum = minimum; | ||
| if (typeof exclusiveMinimum === "number" && ctx.target !== "draft-04") { |
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.
Critical bug: Should be "draft-04" (hyphenated). initializeContext normalizes "draft-4" → "draft-04", so this condition will never match, breaking exclusive minimum handling for draft-04 schemas.
| } | ||
| if (typeof maximum === "number") { | ||
| json.maximum = maximum; | ||
| if (typeof exclusiveMaximum === "number" && ctx.target !== "draft-04") { |
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.
Critical bug: Should be "draft-04" (hyphenated), same issue as line 72 but for exclusive maximum.
| } else { | ||
| if (vals.every((v) => typeof v === "number")) json.type = "number"; | ||
| if (vals.every((v) => typeof v === "string")) json.type = "string"; | ||
| if (vals.every((v) => typeof v === "boolean")) json.type = "string"; |
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.
Bug: Sets json.type = "string" for all-boolean literals. Should be "boolean". This incorrectly marks boolean literal unions.
| return seen.schema; | ||
| } | ||
| // custom method overrides default behavior | ||
| const overrideSchema = schema._zod.toJSONSchema?.(); |
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 custom override (schema._zod.toJSONSchema?.()) bypasses all metadata, override hooks, and transform checks. Consider documenting this behavior, or applying metadata even when custom override is present.
| * Creates a toJSONSchema method for a schema instance. | ||
| * This encapsulates the logic of initializing context, processing, extracting defs, and finalizing. | ||
| */ | ||
| export const createToJSONSchemaMethod = |
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.
Why does createToJSONSchemaMethod default processors to {} while the main toJSONSchema uses allProcessors? This seems like it would break schema conversion. Needs documentation or reconsideration.
| | ||
| export const ZodType: core.$constructor<ZodType> = /*@__PURE__*/ core.$constructor("ZodType", (inst, def) => { | ||
| core.$ZodType.init(inst, def); | ||
| Object.assign(inst["~standard"], { |
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.
Assigns to inst["~standard"] without checking if it exists. If core.$ZodType.init doesn't initialize this property, this will fail. Verify initialization order.
| | ||
| export const pipeProcessor: Processor<schemas.$ZodPipe> = (schema, ctx, _json, params) => { | ||
| const def = schema._zod.def as schemas.$ZodPipeDef; | ||
| const innerType = ctx.io === "input" ? (def.in._zod.def.type === "transform" ? def.out : def.in) : def.out; |
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.
Complex transform handling logic. Add comment explaining why def.in._zod.def.type === "transform" requires using def.out for input mode.
Review CompleteReviewed PR #5477: Add SummaryThis is a well-structured refactoring that moves from class-based to functional JSON Schema generation and adds instance methods. The architecture is solid overall. Issues Found
AssessmentThe refactoring improves modularity and adds useful functionality. Once the draft-04 bugs are fixed, this will be production-ready. The processor pattern is clean and maintainable. All specific feedback has been posted as inline review comments.
|
No description provided.