Skip to content

Commit f97733f

Browse files
committed
Flatten simple intersections
1 parent f23d73c commit f97733f

File tree

3 files changed

+93
-22
lines changed

3 files changed

+93
-22
lines changed

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,3 +1981,69 @@ test("use output type for preprocess", () => {
19811981
// const d = z.string().default("hello");
19821982
// expect(tx(d)).toEqual(false);
19831983
// });
1984+
1985+
test("flatten simple intersections", () => {
1986+
const FirstSchema = z.object({
1987+
testNum: z.number(),
1988+
});
1989+
1990+
const SecondSchema = z.object({
1991+
testStr: z.string(),
1992+
});
1993+
1994+
const ThirdSchema = z.object({
1995+
testBool: z.boolean(),
1996+
});
1997+
1998+
const HelloSchema = FirstSchema.and(SecondSchema).and(ThirdSchema).describe("123");
1999+
2000+
// Zod 3
2001+
// console.log(JSON.stringify(zodToJsonSchema(HelloSchema), null, 2));
2002+
2003+
// Zod 4
2004+
const result = z.toJSONSchema(HelloSchema, { target: "draft-7" });
2005+
expect(result).toMatchInlineSnapshot(`
2006+
{
2007+
"$schema": "http://json-schema.org/draft-07/schema#",
2008+
"allOf": [
2009+
{
2010+
"additionalProperties": false,
2011+
"properties": {
2012+
"testNum": {
2013+
"type": "number",
2014+
},
2015+
},
2016+
"required": [
2017+
"testNum",
2018+
],
2019+
"type": "object",
2020+
},
2021+
{
2022+
"additionalProperties": false,
2023+
"properties": {
2024+
"testStr": {
2025+
"type": "string",
2026+
},
2027+
},
2028+
"required": [
2029+
"testStr",
2030+
],
2031+
"type": "object",
2032+
},
2033+
{
2034+
"additionalProperties": false,
2035+
"properties": {
2036+
"testBool": {
2037+
"type": "boolean",
2038+
},
2039+
},
2040+
"required": [
2041+
"testBool",
2042+
],
2043+
"type": "object",
2044+
},
2045+
],
2046+
"description": "123",
2047+
}
2048+
`);
2049+
});

packages/zod/src/v4/core/to-json-schema.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ export class JSONSchemaGenerator {
105105
seen.cycle = _params.path;
106106
}
107107

108-
seen.count++;
109-
// break cycle
110108
return seen.schema;
111109
}
112110

@@ -320,16 +318,21 @@ export class JSONSchemaGenerator {
320318
}
321319
case "intersection": {
322320
const json: JSONSchema.BaseSchema = _json as any;
323-
json.allOf = [
324-
this.process(def.left, {
325-
...params,
326-
path: [...params.path, "allOf", 0],
327-
}),
328-
this.process(def.right, {
329-
...params,
330-
path: [...params.path, "allOf", 1],
331-
}),
321+
const a = this.process(def.left, {
322+
...params,
323+
path: [...params.path, "allOf", 0],
324+
});
325+
const b = this.process(def.right, {
326+
...params,
327+
path: [...params.path, "allOf", 1],
328+
});
329+
330+
const isSimpleIntersection = (val: any) => "allOf" in val && Object.keys(val).length === 1;
331+
const allOf = [
332+
...(isSimpleIntersection(a) ? (a.allOf as any[]) : [a]),
333+
...(isSimpleIntersection(b) ? (b.allOf as any[]) : [b]),
332334
];
335+
json.allOf = allOf;
333336
break;
334337
}
335338
case "tuple": {

play.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { z } from "zod/v4";
22

3-
// export type ClickCallbackArgs = z.infer<typeof ClickCallbackArgsSchema>;
3+
const FirstSchema = z.object({
4+
testNum: z.number(),
5+
});
46

5-
const myFunction = z.function({
6-
input: [
7-
z.object({
8-
name: z.string(),
9-
age: z.number().int(),
10-
}),
11-
],
12-
output: z.string(),
7+
const SecondSchema = z.object({
8+
testStr: z.string(),
139
});
1410

15-
myFunction.implement((args) => {
16-
return `${args.age}`;
11+
const ThirdSchema = z.object({
12+
testBool: z.boolean(),
1713
});
14+
15+
const HelloSchema = FirstSchema.and(SecondSchema).and(ThirdSchema).describe("123");
16+
17+
// Zod 4
18+
const result = z.toJSONSchema(HelloSchema, { target: "draft-7" });
19+
console.dir(result, { depth: null });

0 commit comments

Comments
 (0)