diff --git a/packages/quicktype-core/src/language/TypeScriptZod/TypeScriptZodRenderer.ts b/packages/quicktype-core/src/language/TypeScriptZod/TypeScriptZodRenderer.ts index 9b20eb1a0c..9eaa0e169f 100644 --- a/packages/quicktype-core/src/language/TypeScriptZod/TypeScriptZodRenderer.ts +++ b/packages/quicktype-core/src/language/TypeScriptZod/TypeScriptZodRenderer.ts @@ -376,13 +376,26 @@ export class TypeScriptZodRenderer extends ConvenienceRenderer { protected emitEnum(e: EnumType, enumName: Name): void { this.ensureBlankLine(); this.emitDescription(this.descriptionForType(e)); - this.emitLine("\nexport const ", enumName, "Schema = ", "z.enum(["); - this.indent(() => - this.forEachEnumCase(e, "none", (_, jsonName) => { - this.emitLine('"', stringEscape(jsonName), '",'); - }), - ); - this.emitLine("]);"); + + if (e.cases.size === 1) { + const value = e.cases.values().next().value; + if (value === undefined) panic("Single-value enum has no case."); + this.emitLine( + "\nexport const ", + enumName, + "Schema = z.literal(", + JSON.stringify(value), + ");", + ); + } else { + this.emitLine("\nexport const ", enumName, "Schema = ", "z.enum(["); + this.indent(() => + this.forEachEnumCase(e, "none", (_, jsonName) => { + this.emitLine('"', stringEscape(jsonName), '",'); + }), + ); + this.emitLine("]);"); + } if (!this._options.justSchema) { this.emitLine( "export type ", diff --git a/test/inputs/schema/single-value-enum.1.fail.enum.json b/test/inputs/schema/single-value-enum.1.fail.enum.json new file mode 100644 index 0000000000..a9805423f7 --- /dev/null +++ b/test/inputs/schema/single-value-enum.1.fail.enum.json @@ -0,0 +1,3 @@ +{ + "kind": "other" +} diff --git a/test/inputs/schema/single-value-enum.1.json b/test/inputs/schema/single-value-enum.1.json new file mode 100644 index 0000000000..a2898d7092 --- /dev/null +++ b/test/inputs/schema/single-value-enum.1.json @@ -0,0 +1,3 @@ +{ + "kind": "only" +} diff --git a/test/inputs/schema/single-value-enum.schema b/test/inputs/schema/single-value-enum.schema new file mode 100644 index 0000000000..5fdf6fa04e --- /dev/null +++ b/test/inputs/schema/single-value-enum.schema @@ -0,0 +1,10 @@ +{ + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": ["only"] + } + }, + "required": ["kind"] +} diff --git a/test/unit/typescript-zod-const-enum.test.ts b/test/unit/typescript-zod-const-enum.test.ts new file mode 100644 index 0000000000..c81a23fcb8 --- /dev/null +++ b/test/unit/typescript-zod-const-enum.test.ts @@ -0,0 +1,31 @@ +import { InputData, JSONSchemaInput, quicktype } from "quicktype-core"; +import { expect, test } from "vitest"; + +async function render(): Promise { + const schemaInput = new JSONSchemaInput(undefined); + await schemaInput.addSource({ + name: "TopLevel", + schema: JSON.stringify({ + type: "object", + properties: { + kind: { type: "string", enum: ["only"] }, + }, + required: ["kind"], + }), + }); + const inputData = new InputData(); + inputData.addInput(schemaInput); + + const result = await quicktype({ + inputData, + lang: "typescript-zod", + }); + return result.lines.join("\n"); +} + +test("TypeScript Zod emits a literal for a single-value enum", async () => { + const output = await render(); + + expect(output).toContain('z.literal("only")'); + expect(output).not.toContain("z.enum(["); +});