Skip to content

Commit 767f320

Browse files
authored
Add .toJSONSchema() method (#5477)
* Checkpoint * Checkpoint * Implement toJSONSchema method * Implement Standard JSON Schema * Clean up * Add back files * Revert "Add back files" This reverts commit 3e8ba43. * Fix resolution bug * Tweak * Test * Refactor files * Address reviews * Address pullfrog reviews
1 parent 325a701 commit 767f320

File tree

14 files changed

+1974
-907
lines changed

14 files changed

+1974
-907
lines changed

packages/docs/content/json-schema.mdx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,18 @@ Below is a quick reference for each supported parameter. Each one is explained i
215215
interface ToJSONSchemaParams {
216216
/** The JSON Schema version to target.
217217
* - `"draft-2020-12"` — Default. JSON Schema Draft 2020-12
218-
* - `"draft-7"` — JSON Schema Draft 7
219-
* - `"draft-4"` — JSON Schema Draft 4
218+
* - `"draft-07"` — JSON Schema Draft 7
219+
* - `"draft-04"` — JSON Schema Draft 4
220220
* - `"openapi-3.0"` — OpenAPI 3.0 Schema Object */
221-
target?: "draft-4" | "draft-7" | "draft-2020-12" | "openapi-3.0";
221+
target?:
222+
| "draft-04"
223+
| "draft-4"
224+
| "draft-07"
225+
| "draft-7"
226+
| "draft-2020-12"
227+
| "openapi-3.0"
228+
| ({} & string)
229+
| undefined;
222230

223231
/** A registry used to look up metadata for each schema.
224232
* Any schema with an `id` property will be extracted as a $def. */
@@ -251,9 +259,9 @@ interface ToJSONSchemaParams {
251259
To set the target JSON Schema version, use the `target` parameter. By default, Zod will target Draft 2020-12.
252260

253261
```ts
254-
z.toJSONSchema(schema, { target: "draft-7" });
262+
z.toJSONSchema(schema, { target: "draft-07" });
255263
z.toJSONSchema(schema, { target: "draft-2020-12" });
256-
z.toJSONSchema(schema, { target: "draft-4" });
264+
z.toJSONSchema(schema, { target: "draft-04" });
257265
z.toJSONSchema(schema, { target: "openapi-3.0" });
258266
```
259267

packages/zod/src/v4/classic/schemas.ts

Lines changed: 97 additions & 5 deletions
Large diffs are not rendered by default.

packages/zod/src/v4/classic/tests/json.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { test } from "vitest";
2-
// import * as z from "zod/v4";
1+
import { expect, test } from "vitest";
32

4-
test(() => {});
3+
test(() => {
4+
expect(2).toBe(2);
5+
});
56
// test("overload types", () => {
67
// const schema = z.string().json();
78
// util.assertEqual<typeof schema, z.ZodString>(true);

packages/zod/src/v4/classic/tests/standard-schema.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,80 @@ test("length checks", async () => {
5555
}
5656
`);
5757
});
58+
59+
test("schemas conform to StandardJSONSchemaV1", async () => {
60+
const schema = z.codec(z.string(), z.number(), {
61+
decode: (str) => Number.parseFloat(str),
62+
encode: (num) => num.toString(),
63+
});
64+
expect(schema["~standard"].validate).toBeTypeOf("function");
65+
expect(await schema["~standard"].validate("42")).toMatchInlineSnapshot(`
66+
{
67+
"value": 42,
68+
}
69+
`);
70+
expect(schema["~standard"].jsonSchema.input({ target: "draft-2020-12" })).toMatchInlineSnapshot(`
71+
{
72+
"$schema": "https://json-schema.org/draft/2020-12/schema",
73+
"type": "string",
74+
}
75+
`);
76+
expect(schema["~standard"].jsonSchema.output({ target: "draft-2020-12" })).toMatchInlineSnapshot(`
77+
{
78+
"$schema": "https://json-schema.org/draft/2020-12/schema",
79+
"type": "number",
80+
}
81+
`);
82+
});
83+
84+
test(".toJSONSchema() returns StandardJSONSchemaV1", async () => {
85+
const codec = z.codec(z.string(), z.number(), {
86+
decode: (str) => Number.parseFloat(str),
87+
encode: (num) => num.toString(),
88+
});
89+
const result = codec.toJSONSchema();
90+
expect(result["~standard"].validate).toBeTypeOf("function");
91+
expect(await result["~standard"].validate("42")).toMatchInlineSnapshot(`
92+
{
93+
"value": 42,
94+
}
95+
`);
96+
expect(result["~standard"].jsonSchema.input({ target: "draft-2020-12" })).toMatchInlineSnapshot(`
97+
{
98+
"$schema": "https://json-schema.org/draft/2020-12/schema",
99+
"type": "string",
100+
}
101+
`);
102+
expect(result["~standard"].jsonSchema.output({ target: "draft-2020-12" })).toMatchInlineSnapshot(`
103+
{
104+
"$schema": "https://json-schema.org/draft/2020-12/schema",
105+
"type": "number",
106+
}
107+
`);
108+
});
109+
110+
test("z.toJSONSchema() returns StandardJSONSchemaV1", async () => {
111+
const codec = z.codec(z.string(), z.number(), {
112+
decode: (str) => Number.parseFloat(str),
113+
encode: (num) => num.toString(),
114+
});
115+
const result = z.toJSONSchema(codec);
116+
expect(result["~standard"].validate).toBeTypeOf("function");
117+
expect(await result["~standard"].validate("42")).toMatchInlineSnapshot(`
118+
{
119+
"value": 42,
120+
}
121+
`);
122+
expect(result["~standard"].jsonSchema.input({ target: "draft-2020-12" })).toMatchInlineSnapshot(`
123+
{
124+
"$schema": "https://json-schema.org/draft/2020-12/schema",
125+
"type": "string",
126+
}
127+
`);
128+
expect(result["~standard"].jsonSchema.output({ target: "draft-2020-12" })).toMatchInlineSnapshot(`
129+
{
130+
"$schema": "https://json-schema.org/draft/2020-12/schema",
131+
"type": "number",
132+
}
133+
`);
134+
});

0 commit comments

Comments
 (0)