Skip to content

Commit ffc41bd

Browse files
committed
Improve JSON Schema typing
1 parent d3389cb commit ffc41bd

File tree

6 files changed

+535
-488
lines changed

6 files changed

+535
-488
lines changed

packages/zod/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zod",
3-
"version": "3.25.58",
3+
"version": "3.25.59",
44
"type": "module",
55
"author": "Colin McDonnell <[email protected]>",
66
"description": "TypeScript-first schema declaration and validation library with static type inference",

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ test("override with refs", () => {
12211221
const result = z.z.toJSONSchema(a, {
12221222
override(ctx) {
12231223
if (ctx.zodSchema._zod.def.type === "string") {
1224-
ctx.jsonSchema.type = "STRING";
1224+
ctx.jsonSchema.type = "STRING" as "string";
12251225
}
12261226
},
12271227
});
@@ -2164,3 +2164,19 @@ test("z.file()", () => {
21642164
}
21652165
`);
21662166
});
2167+
2168+
test("custom toJSONSchema", () => {
2169+
const schema = z.instanceof(Date);
2170+
schema._zod.toJSONSchema = () => ({
2171+
type: "string",
2172+
format: "date-time",
2173+
});
2174+
2175+
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
2176+
{
2177+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2178+
"format": "date-time",
2179+
"type": "string",
2180+
}
2181+
`);
2182+
});

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

Lines changed: 103 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,101 +6,138 @@ export type Schema =
66
| IntegerSchema
77
| BooleanSchema
88
| NullSchema;
9-
// | BaseSchema;
109

11-
export interface BaseSchema {
12-
type?: string | undefined;
10+
// export type JsonType = "object" | "array" | "string" | "number" | "boolean" | "null" | "integer";
11+
12+
// export interface JSONSchema {
13+
// type?: string | undefined;
14+
// $id?: string | undefined;
15+
// id?: string | undefined;
16+
// $schema?: string | undefined;
17+
// $ref?: string | undefined;
18+
// $anchor?: string | undefined;
19+
// $defs?: { [key: string]: JSONSchema } | undefined;
20+
// definitions?: { [key: string]: JSONSchema } | undefined;
21+
// $comment?: string | undefined;
22+
// title?: string | undefined;
23+
// description?: string | undefined;
24+
// default?: unknown | undefined;
25+
// examples?: unknown[] | undefined;
26+
// readOnly?: boolean | undefined;
27+
// writeOnly?: boolean | undefined;
28+
// deprecated?: boolean | undefined;
29+
// allOf?: JSONSchema[] | undefined;
30+
// anyOf?: JSONSchema[] | undefined;
31+
// oneOf?: JSONSchema[] | undefined;
32+
// not?: JSONSchema | undefined;
33+
// if?: JSONSchema | undefined;
34+
// then?: JSONSchema | undefined;
35+
// else?: JSONSchema | undefined;
36+
// enum?: Array<string | number | boolean | null> | undefined;
37+
// const?: string | number | boolean | null | undefined;
38+
// [k: string]: unknown;
39+
40+
// /** A special key used as an intermediate representation of extends-style relationships. Omitted as a $ref with additional properties. */
41+
// // _ref?: JSONSchema;
42+
// _prefault?: unknown | undefined;
43+
// }
44+
45+
export type _JSONSchema = boolean | JSONSchema;
46+
export type JSONSchema = {
47+
[k: string]: unknown;
48+
$schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#" | undefined;
1349
$id?: string | undefined;
14-
id?: string | undefined;
15-
$schema?: string | undefined;
16-
$ref?: string | undefined;
1750
$anchor?: string | undefined;
18-
$defs?: { [key: string]: BaseSchema } | undefined;
19-
definitions?: { [key: string]: BaseSchema } | undefined;
51+
$ref?: string | undefined;
52+
$dynamicRef?: string | undefined;
53+
$dynamicAnchor?: string | undefined;
54+
$vocabulary?: Record<string, boolean> | undefined;
2055
$comment?: string | undefined;
56+
$defs?: Record<string, JSONSchema> | undefined;
57+
type?: "object" | "array" | "string" | "number" | "boolean" | "null" | "integer" | undefined;
58+
additionalItems?: _JSONSchema | undefined;
59+
unevaluatedItems?: _JSONSchema | undefined;
60+
prefixItems?: _JSONSchema[] | undefined;
61+
items?: _JSONSchema | _JSONSchema[] | undefined;
62+
contains?: _JSONSchema | undefined;
63+
additionalProperties?: _JSONSchema | undefined;
64+
unevaluatedProperties?: _JSONSchema | undefined;
65+
properties?: Record<string, _JSONSchema> | undefined;
66+
patternProperties?: Record<string, _JSONSchema> | undefined;
67+
dependentSchemas?: Record<string, _JSONSchema> | undefined;
68+
propertyNames?: _JSONSchema | undefined;
69+
if?: _JSONSchema | undefined;
70+
then?: _JSONSchema | undefined;
71+
else?: _JSONSchema | undefined;
72+
allOf?: JSONSchema[] | undefined;
73+
anyOf?: JSONSchema[] | undefined;
74+
oneOf?: JSONSchema[] | undefined;
75+
not?: _JSONSchema | undefined;
76+
multipleOf?: number | undefined;
77+
maximum?: number | undefined;
78+
exclusiveMaximum?: number | undefined;
79+
minimum?: number | undefined;
80+
exclusiveMinimum?: number | undefined;
81+
maxLength?: number | undefined;
82+
minLength?: number | undefined;
83+
pattern?: string | undefined;
84+
maxItems?: number | undefined;
85+
minItems?: number | undefined;
86+
uniqueItems?: boolean | undefined;
87+
maxContains?: number | undefined;
88+
minContains?: number | undefined;
89+
maxProperties?: number | undefined;
90+
minProperties?: number | undefined;
91+
required?: string[] | undefined;
92+
dependentRequired?: Record<string, string[]> | undefined;
93+
enum?: Array<string | number | boolean | null> | undefined;
94+
const?: string | number | boolean | null | undefined;
95+
96+
// metadata
97+
id?: string | undefined;
2198
title?: string | undefined;
2299
description?: string | undefined;
23100
default?: unknown | undefined;
24-
examples?: unknown[] | undefined;
101+
deprecated?: boolean | undefined;
25102
readOnly?: boolean | undefined;
26103
writeOnly?: boolean | undefined;
27-
deprecated?: boolean | undefined;
28-
allOf?: BaseSchema[] | undefined;
29-
anyOf?: BaseSchema[] | undefined;
30-
oneOf?: BaseSchema[] | undefined;
31-
not?: BaseSchema | undefined;
32-
if?: BaseSchema | undefined;
33-
then?: BaseSchema | undefined;
34-
else?: BaseSchema | undefined;
35-
enum?: Array<string | number | boolean | null> | undefined;
36-
const?: string | number | boolean | null | undefined;
37-
[k: string]: unknown;
104+
examples?: unknown[] | undefined;
105+
format?: string | undefined;
106+
contentMediaType?: string | undefined;
107+
contentEncoding?: string | undefined;
108+
contentSchema?: JSONSchema | undefined;
38109

39-
/** A special key used as an intermediate representation of extends-style relationships. Omitted as a $ref with additional properties. */
40-
// _ref?: BaseSchema;
110+
// internal
41111
_prefault?: unknown | undefined;
42-
}
112+
};
43113

44-
export interface ObjectSchema extends BaseSchema {
114+
// for backwards compatibility
115+
export type BaseSchema = JSONSchema;
116+
117+
export interface ObjectSchema extends JSONSchema {
45118
type: "object";
46-
properties?: { [key: string]: BaseSchema } | undefined;
47-
patternProperties?: { [key: string]: BaseSchema } | undefined;
48-
additionalProperties?: BaseSchema | boolean | undefined;
49-
required?: string[] | undefined;
50-
dependentRequired?: { [key: string]: string[] } | undefined;
51-
propertyNames?: BaseSchema | undefined;
52-
minProperties?: number | undefined;
53-
maxProperties?: number | undefined;
54-
unevaluatedProperties?: BaseSchema | boolean | undefined;
55-
dependentSchemas?: { [key: string]: BaseSchema } | undefined;
56119
}
57120

58-
export interface ArraySchema extends BaseSchema {
121+
export interface ArraySchema extends JSONSchema {
59122
type: "array";
60-
items?: BaseSchema | BaseSchema[] | undefined;
61-
prefixItems?: BaseSchema[] | undefined;
62-
additionalItems?: BaseSchema | boolean; // (deprecated | undefined)
63-
contains?: BaseSchema | undefined;
64-
minItems?: number | undefined;
65-
maxItems?: number | undefined;
66-
minContains?: number | undefined;
67-
maxContains?: number | undefined;
68-
uniqueItems?: boolean | undefined;
69-
unevaluatedItems?: BaseSchema | boolean | undefined;
70123
}
71124

72-
export interface StringSchema extends BaseSchema {
125+
export interface StringSchema extends JSONSchema {
73126
type: "string";
74-
minLength?: number | undefined;
75-
maxLength?: number | undefined;
76-
pattern?: string | undefined;
77-
format?: string | undefined;
78-
contentEncoding?: string | undefined;
79-
contentMediaType?: string | undefined;
80127
}
81128

82-
export interface NumberSchema extends BaseSchema {
129+
export interface NumberSchema extends JSONSchema {
83130
type: "number";
84-
minimum?: number | undefined;
85-
maximum?: number | undefined;
86-
exclusiveMinimum?: number | undefined;
87-
exclusiveMaximum?: number | undefined;
88-
multipleOf?: number | undefined;
89131
}
90132

91-
export interface IntegerSchema extends BaseSchema {
133+
export interface IntegerSchema extends JSONSchema {
92134
type: "integer";
93-
minimum?: number | undefined;
94-
maximum?: number | undefined;
95-
exclusiveMinimum?: number | undefined;
96-
exclusiveMaximum?: number | undefined;
97-
multipleOf?: number | undefined;
98135
}
99136

100-
export interface BooleanSchema extends BaseSchema {
137+
export interface BooleanSchema extends JSONSchema {
101138
type: "boolean";
102139
}
103140

104-
export interface NullSchema extends BaseSchema {
141+
export interface NullSchema extends JSONSchema {
105142
type: "null";
106143
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export interface _$ZodTypeInternals {
139139
isst: errors.$ZodIssueBase;
140140

141141
/** An optional method used to override `toJSONSchema` logic. */
142-
toJSONSchema?: () => object;
142+
toJSONSchema?: () => unknown;
143143

144144
/** @internal The parent of this schema. Only set during certain clone operations. */
145145
parent?: $ZodType | undefined;

0 commit comments

Comments
 (0)