From 58b9d36f72a67b4499ccc040606e6814953e9d47 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 17:26:23 -0400 Subject: [PATCH 1/4] fix(schema): honor single-entry patternProperties as additionalProperties (#1643) quicktype only used patternProperties in place of additionalProperties when the pattern key was the literal string ".*". Any other pattern (e.g. "(^[A-Za-z0-9]+$)"), including ones that also match every string, was silently ignored, leaving the property with no type information and generating JSONAny/[String: JSONAny] instead of a typed map. Generalize the existing hack: when additionalProperties is unset and patternProperties has exactly one entry, use that entry's schema as the effective additionalProperties, regardless of the pattern text. Multiple patternProperties entries are left as before (unsupported). Added a JSON Schema fixture (test/inputs/schema/pattern-properties.schema + .1.json) covering the reported shape, and a focused unit test asserting the generated Swift output has a typed dictionary value instead of JSONAny. Fixes #1643 Co-Authored-By: gpt-5.6-sol via pi --- .../src/input/JSONSchemaInput.ts | 14 +++++-- test/inputs/schema/pattern-properties.1.json | 12 ++++++ test/inputs/schema/pattern-properties.schema | 19 +++++++++ test/unit/pattern-properties-schema.test.ts | 41 +++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 test/inputs/schema/pattern-properties.1.json create mode 100644 test/inputs/schema/pattern-properties.schema create mode 100644 test/unit/pattern-properties-schema.test.ts diff --git a/packages/quicktype-core/src/input/JSONSchemaInput.ts b/packages/quicktype-core/src/input/JSONSchemaInput.ts index d3a0e4feef..0b93239f19 100644 --- a/packages/quicktype-core/src/input/JSONSchemaInput.ts +++ b/packages/quicktype-core/src/input/JSONSchemaInput.ts @@ -1129,14 +1129,20 @@ async function addTypesInSchema( } let additionalProperties = schema.additionalProperties; - // This is an incorrect hack to fix an issue with a Go->Schema generator: - // https://github.com/quicktype/quicktype/issues/976 + // Treat a single pattern property as additional properties since + // per-pattern typed properties aren't supported. if ( additionalProperties === undefined && typeof schema.patternProperties === "object" && - hasOwnProperty(schema.patternProperties, ".*") + schema.patternProperties !== null ) { - additionalProperties = schema.patternProperties[".*"]; + const patterns = Object.getOwnPropertyNames( + schema.patternProperties, + ); + if (patterns.length === 1) { + additionalProperties = + schema.patternProperties[patterns[0]]; + } } const objectAttributes = combineTypeAttributes( diff --git a/test/inputs/schema/pattern-properties.1.json b/test/inputs/schema/pattern-properties.1.json new file mode 100644 index 0000000000..fda2c99154 --- /dev/null +++ b/test/inputs/schema/pattern-properties.1.json @@ -0,0 +1,12 @@ +{ + "alternators": { + "1": { + "voltage": 14.2, + "name": "Main alternator" + }, + "backup": { + "voltage": 13.8, + "name": "Backup alternator" + } + } +} diff --git a/test/inputs/schema/pattern-properties.schema b/test/inputs/schema/pattern-properties.schema new file mode 100644 index 0000000000..c22ba2338d --- /dev/null +++ b/test/inputs/schema/pattern-properties.schema @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Electrical", + "type": "object", + "properties": { + "alternators": { + "type": "object", + "patternProperties": { + "(^[A-Za-z0-9]+$)": { + "type": "object", + "properties": { + "voltage": { "type": "number" }, + "name": { "type": "string" } + } + } + } + } + } +} diff --git a/test/unit/pattern-properties-schema.test.ts b/test/unit/pattern-properties-schema.test.ts new file mode 100644 index 0000000000..75bd39ce69 --- /dev/null +++ b/test/unit/pattern-properties-schema.test.ts @@ -0,0 +1,41 @@ +import { + InputData, + JSONSchemaInput, + quicktype, +} from "../../packages/quicktype-core/src/index.js"; +import { expect, test } from "vitest"; + +const schema = JSON.stringify({ + type: "object", + properties: { + alternators: { + type: "object", + patternProperties: { + "(^[A-Za-z0-9]+$)": { + type: "object", + properties: { + voltage: { type: "number" }, + name: { type: "string" }, + }, + }, + }, + }, + }, +}); + +test("uses a single pattern property as the map value type", async () => { + const schemaInput = new JSONSchemaInput(undefined); + await schemaInput.addSource({ name: "Electrical", schema }); + + const inputData = new InputData(); + inputData.addInput(schemaInput); + + const result = await quicktype({ inputData, lang: "swift" }); + const output = result.lines.join("\n"); + + expect(output).toContain("let alternators: [String: Alternator]?"); + expect(output).toContain("struct Alternator: Codable"); + expect(output).toContain("let name: String?"); + expect(output).toContain("let voltage: Double?"); + expect(output).not.toContain("JSONAny"); +}); From a6cd13967a2a963c80cb25a248a838d47b0097e8 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 19:40:32 -0400 Subject: [PATCH 2/4] test: add missing fixture cases for pattern-properties.schema (#3008) Co-Authored-By: Claude --- test/inputs/schema/pattern-properties.1.fail.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/inputs/schema/pattern-properties.1.fail.json diff --git a/test/inputs/schema/pattern-properties.1.fail.json b/test/inputs/schema/pattern-properties.1.fail.json new file mode 100644 index 0000000000..088c5faddb --- /dev/null +++ b/test/inputs/schema/pattern-properties.1.fail.json @@ -0,0 +1,8 @@ +{ + "alternators": { + "1": { + "voltage": "not a number", + "name": "Main alternator" + } + } +} From 0dd0e7ba3de300909f94e60d4154a87af02b39d2 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 20:30:08 -0400 Subject: [PATCH 3/4] test: remove unit test coverage duplicated by fixtures (#undefined) Co-Authored-By: Claude --- test/unit/pattern-properties-schema.test.ts | 41 --------------------- 1 file changed, 41 deletions(-) delete mode 100644 test/unit/pattern-properties-schema.test.ts diff --git a/test/unit/pattern-properties-schema.test.ts b/test/unit/pattern-properties-schema.test.ts deleted file mode 100644 index 75bd39ce69..0000000000 --- a/test/unit/pattern-properties-schema.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - InputData, - JSONSchemaInput, - quicktype, -} from "../../packages/quicktype-core/src/index.js"; -import { expect, test } from "vitest"; - -const schema = JSON.stringify({ - type: "object", - properties: { - alternators: { - type: "object", - patternProperties: { - "(^[A-Za-z0-9]+$)": { - type: "object", - properties: { - voltage: { type: "number" }, - name: { type: "string" }, - }, - }, - }, - }, - }, -}); - -test("uses a single pattern property as the map value type", async () => { - const schemaInput = new JSONSchemaInput(undefined); - await schemaInput.addSource({ name: "Electrical", schema }); - - const inputData = new InputData(); - inputData.addInput(schemaInput); - - const result = await quicktype({ inputData, lang: "swift" }); - const output = result.lines.join("\n"); - - expect(output).toContain("let alternators: [String: Alternator]?"); - expect(output).toContain("struct Alternator: Codable"); - expect(output).toContain("let name: String?"); - expect(output).toContain("let voltage: Double?"); - expect(output).not.toContain("JSONAny"); -}); From 24a18cbff65d2d5d15a9ca8d55afa1cfa5790db6 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 20:36:59 -0400 Subject: [PATCH 4/4] test: fix gating of pattern-properties fixture cases (#3008) cjson does not validate class-element types, so the mistyped map value in pattern-properties.1.fail.json is not rejected. Skip the schema for cjson, matching the existing go-schema-pattern-properties handling. Co-Authored-By: Claude --- test/languages.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/languages.ts b/test/languages.ts index 6bcfd43611..0952905a66 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -577,6 +577,7 @@ export const CJSONLanguage: Language = { /* Union, Map and Arrays with invalid types are not checked (for the current implementation, can be added later, should abord parsing and return NULL) */ "class-with-additional.schema", "go-schema-pattern-properties.schema", + "pattern-properties.schema", "multi-type-enum.schema", "nested-intersection-union.schema", "prefix-items.schema",