quicktype-core 23.3.21 → 23.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -8,6 +8,10 @@ import { type ClassProperty, type EnumType, ObjectType, type Type } from "../../
|
|
|
8
8
|
import type { typeScriptZodOptions } from "./language";
|
|
9
9
|
export declare class TypeScriptZodRenderer extends ConvenienceRenderer {
|
|
10
10
|
protected readonly _options: OptionValues<typeof typeScriptZodOptions>;
|
|
11
|
+
/** TypeRefs of object types that participate in a reference cycle.
|
|
12
|
+
* These must be emitted as z.lazy() schemas with an explicit type
|
|
13
|
+
* annotation, since zod cannot infer recursive types. */
|
|
14
|
+
private _recursiveTypeRefs;
|
|
11
15
|
constructor(targetLanguage: TargetLanguage, renderContext: RenderContext, _options: OptionValues<typeof typeScriptZodOptions>);
|
|
12
16
|
protected forbiddenNamesForGlobalNamespace(): string[];
|
|
13
17
|
protected nameStyle(original: string, upper: boolean): string;
|
|
@@ -19,7 +23,23 @@ export declare class TypeScriptZodRenderer extends ConvenienceRenderer {
|
|
|
19
23
|
protected emitImports(): void;
|
|
20
24
|
protected typeMapTypeForProperty(p: ClassProperty): Sourcelike;
|
|
21
25
|
protected typeMapTypeFor(t: Type, required?: boolean): Sourcelike;
|
|
26
|
+
/** TypeScript type for `t`, used to annotate recursive schemas for
|
|
27
|
+
* which zod cannot infer the type.
|
|
28
|
+
*
|
|
29
|
+
* This intentionally duplicates neither `typeMapTypeFor` above nor
|
|
30
|
+
* `sourceFor` in the plain TypeScript renderer
|
|
31
|
+
* (`TypeScriptFlowBaseRenderer`): it must mirror exactly what
|
|
32
|
+
* `z.infer` would derive from the schemas emitted by
|
|
33
|
+
* `typeMapTypeFor` (e.g. `z.coerce.date()` implies `Date`), while
|
|
34
|
+
* `sourceFor` is shaped by ts/flow-specific options like
|
|
35
|
+
* `preferConstValues` and `declareUnions` that don't exist here. */
|
|
36
|
+
protected underlyingTypeFor(t: Type): Sourcelike;
|
|
37
|
+
protected isRecursive(t: ObjectType): boolean;
|
|
22
38
|
protected emitObject(name: Name, t: ObjectType): void;
|
|
39
|
+
/** Emit a recursive object type. zod cannot infer recursive types, so
|
|
40
|
+
* the type is declared explicitly and the schema is wrapped in
|
|
41
|
+
* z.lazy() with a z.ZodType annotation. */
|
|
42
|
+
protected emitLazyObject(name: Name, t: ObjectType): void;
|
|
23
43
|
protected emitEnum(e: EnumType, enumName: Name): void;
|
|
24
44
|
/** Static function that extracts underlying type refs for types that form part of the
|
|
25
45
|
* definition of the passed type - used to ensure that these appear in generated source
|
|
@@ -14,6 +14,10 @@ class TypeScriptZodRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
|
|
|
14
14
|
constructor(targetLanguage, renderContext, _options) {
|
|
15
15
|
super(targetLanguage, renderContext);
|
|
16
16
|
this._options = _options;
|
|
17
|
+
/** TypeRefs of object types that participate in a reference cycle.
|
|
18
|
+
* These must be emitted as z.lazy() schemas with an explicit type
|
|
19
|
+
* annotation, since zod cannot infer recursive types. */
|
|
20
|
+
this._recursiveTypeRefs = new Set();
|
|
17
21
|
}
|
|
18
22
|
forbiddenNamesForGlobalNamespace() {
|
|
19
23
|
return ["Class", "Date", "Object", "String", "Array", "JSON", "Error"];
|
|
@@ -72,7 +76,46 @@ class TypeScriptZodRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
|
|
|
72
76
|
}
|
|
73
77
|
return match;
|
|
74
78
|
}
|
|
79
|
+
/** TypeScript type for `t`, used to annotate recursive schemas for
|
|
80
|
+
* which zod cannot infer the type.
|
|
81
|
+
*
|
|
82
|
+
* This intentionally duplicates neither `typeMapTypeFor` above nor
|
|
83
|
+
* `sourceFor` in the plain TypeScript renderer
|
|
84
|
+
* (`TypeScriptFlowBaseRenderer`): it must mirror exactly what
|
|
85
|
+
* `z.infer` would derive from the schemas emitted by
|
|
86
|
+
* `typeMapTypeFor` (e.g. `z.coerce.date()` implies `Date`), while
|
|
87
|
+
* `sourceFor` is shaped by ts/flow-specific options like
|
|
88
|
+
* `preferConstValues` and `declareUnions` that don't exist here. */
|
|
89
|
+
underlyingTypeFor(t) {
|
|
90
|
+
if (["class", "object", "enum"].includes(t.kind)) {
|
|
91
|
+
return this.nameForNamedType(t);
|
|
92
|
+
}
|
|
93
|
+
return (0, TypeUtils_1.matchType)(t, (_anyType) => "any", (_nullType) => "null", (_boolType) => "boolean", (_integerType) => "number", (_doubleType) => "number", (_stringType) => "string", (arrayType) => [
|
|
94
|
+
"Array<",
|
|
95
|
+
this.underlyingTypeFor(arrayType.items),
|
|
96
|
+
">",
|
|
97
|
+
], (_classType) => (0, Support_1.panic)("Should already be handled."), (mapType) => [
|
|
98
|
+
"Record<string, ",
|
|
99
|
+
this.underlyingTypeFor(mapType.values),
|
|
100
|
+
">",
|
|
101
|
+
], (_enumType) => (0, Support_1.panic)("Should already be handled."), (unionType) => {
|
|
102
|
+
const children = Array.from(unionType.getChildren()).map((type) => this.underlyingTypeFor(type));
|
|
103
|
+
return (0, collection_utils_1.arrayIntercalate)(" | ", children);
|
|
104
|
+
}, (_transformedStringType) => {
|
|
105
|
+
if (_transformedStringType.kind === "date-time") {
|
|
106
|
+
return "Date";
|
|
107
|
+
}
|
|
108
|
+
return "string";
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
isRecursive(t) {
|
|
112
|
+
return this._recursiveTypeRefs.has(t.typeRef);
|
|
113
|
+
}
|
|
75
114
|
emitObject(name, t) {
|
|
115
|
+
if (this.isRecursive(t)) {
|
|
116
|
+
this.emitLazyObject(name, t);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
76
119
|
this.ensureBlankLine();
|
|
77
120
|
this.emitLine("\nexport const ", name, "Schema = ", "z.object({");
|
|
78
121
|
this.indent(() => {
|
|
@@ -85,6 +128,35 @@ class TypeScriptZodRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
|
|
|
85
128
|
this.emitLine("export type ", name, " = z.infer<typeof ", name, "Schema>;");
|
|
86
129
|
}
|
|
87
130
|
}
|
|
131
|
+
/** Emit a recursive object type. zod cannot infer recursive types, so
|
|
132
|
+
* the type is declared explicitly and the schema is wrapped in
|
|
133
|
+
* z.lazy() with a z.ZodType annotation. */
|
|
134
|
+
emitLazyObject(name, t) {
|
|
135
|
+
this.ensureBlankLine();
|
|
136
|
+
if (this._options.justSchema) {
|
|
137
|
+
this.emitLine("\nexport const ", name, "Schema: z.ZodType<any> = z.lazy(() =>");
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
this.emitLine("\nexport type ", name, " = {");
|
|
141
|
+
this.indent(() => {
|
|
142
|
+
this.forEachClassProperty(t, "none", (_, jsonName, property) => {
|
|
143
|
+
this.emitLine(`"${(0, Strings_1.utf16StringEscape)(jsonName)}"`, property.isOptional ? "?" : "", ": ", this.underlyingTypeFor(property.type), ";");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
this.emitLine("};");
|
|
147
|
+
this.emitLine("export const ", name, "Schema: z.ZodType<", name, "> = z.lazy(() =>");
|
|
148
|
+
}
|
|
149
|
+
this.indent(() => {
|
|
150
|
+
this.emitLine("z.object({");
|
|
151
|
+
this.indent(() => {
|
|
152
|
+
this.forEachClassProperty(t, "none", (_, jsonName, property) => {
|
|
153
|
+
this.emitLine(`"${(0, Strings_1.utf16StringEscape)(jsonName)}"`, ": ", this.typeMapTypeForProperty(property), ",");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
this.emitLine("})");
|
|
157
|
+
});
|
|
158
|
+
this.emitLine(");");
|
|
159
|
+
}
|
|
88
160
|
emitEnum(e, enumName) {
|
|
89
161
|
this.ensureBlankLine();
|
|
90
162
|
this.emitDescription(this.descriptionForType(e));
|
|
@@ -164,10 +236,44 @@ class TypeScriptZodRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
|
|
|
164
236
|
});
|
|
165
237
|
mapChildTypeRefs.push(childTypeRefs);
|
|
166
238
|
});
|
|
239
|
+
// Find types that participate in a reference cycle: types from
|
|
240
|
+
// which we can get back to the same type by following child type
|
|
241
|
+
// references. Those can never be topologically ordered.
|
|
242
|
+
const indexForTypeRef = new Map();
|
|
243
|
+
mapTypeRef.forEach((typeRef, index) => indexForTypeRef.set(typeRef, index));
|
|
244
|
+
this._recursiveTypeRefs = new Set();
|
|
245
|
+
mapType.forEach((_, startIndex) => {
|
|
246
|
+
const visited = new Set();
|
|
247
|
+
const stack = [...mapChildTypeRefs[startIndex]];
|
|
248
|
+
while (stack.length > 0) {
|
|
249
|
+
const childRef = stack.pop();
|
|
250
|
+
const childIndex = indexForTypeRef.get(childRef);
|
|
251
|
+
if (childIndex === undefined)
|
|
252
|
+
continue;
|
|
253
|
+
if (childIndex === startIndex) {
|
|
254
|
+
this._recursiveTypeRefs.add(mapTypeRef[startIndex]);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
if (visited.has(childIndex))
|
|
258
|
+
continue;
|
|
259
|
+
visited.add(childIndex);
|
|
260
|
+
stack.push(...mapChildTypeRefs[childIndex]);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
167
263
|
// Items to process on this pass
|
|
168
264
|
let indices = [];
|
|
169
265
|
mapType.forEach((_, index) => {
|
|
170
|
-
|
|
266
|
+
if (this._recursiveTypeRefs.has(mapTypeRef[index])) {
|
|
267
|
+
// Recursive types are emitted first: they are wrapped in
|
|
268
|
+
// z.lazy(), so all their references to other schemas are
|
|
269
|
+
// deferred until parse time and don't constrain the
|
|
270
|
+
// output order, while other schemas can reference them
|
|
271
|
+
// directly.
|
|
272
|
+
order.push(index);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
indices.push(index);
|
|
276
|
+
}
|
|
171
277
|
});
|
|
172
278
|
// items to process on the next pass
|
|
173
279
|
let deferredIndices = [];
|
|
@@ -214,8 +320,9 @@ class TypeScriptZodRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
|
|
|
214
320
|
deferredIndices = [];
|
|
215
321
|
passNum++;
|
|
216
322
|
if (passNum > MAX_PASSES) {
|
|
217
|
-
// giving up
|
|
218
|
-
|
|
323
|
+
// giving up: emit the stuck items anyway rather than
|
|
324
|
+
// silently dropping them
|
|
325
|
+
order.push(...indices);
|
|
219
326
|
console.warn("Exceeded maximum number of passes when determining output order, output may contain forward references");
|
|
220
327
|
}
|
|
221
328
|
} while (indices.length > 0 && passNum <= MAX_PASSES);
|