From d649554c9d961a9b84cb487d1e128f0dd585fcb7 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 17:58:41 -0400 Subject: [PATCH 1/5] fix(schema): infer patternProperties value type for any single pattern (#1464) The additionalProperties/patternProperties hack added for #976 only kicked in when the pattern was literally ".*", so any other single regex (e.g. "^[a-zA-Z]\w*$") fell back to an untyped map value (nlohmann::json/any) instead of the schema's declared type. Generalize the hack to apply whenever patternProperties has exactly one entry, regardless of the pattern text, since quicktype does not validate property names against the pattern anyway. Co-Authored-By: gpt-5.6-sol via pi --- .../quicktype-core/src/input/JSONSchemaInput.ts | 14 +++++++++++--- test/inputs/schema/pattern-properties.1.fail.json | 3 +++ test/inputs/schema/pattern-properties.1.json | 3 +++ test/inputs/schema/pattern-properties.schema | 9 +++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test/inputs/schema/pattern-properties.1.fail.json create mode 100644 test/inputs/schema/pattern-properties.1.json create mode 100644 test/inputs/schema/pattern-properties.schema diff --git a/packages/quicktype-core/src/input/JSONSchemaInput.ts b/packages/quicktype-core/src/input/JSONSchemaInput.ts index 893893d0ad..f443c156a2 100644 --- a/packages/quicktype-core/src/input/JSONSchemaInput.ts +++ b/packages/quicktype-core/src/input/JSONSchemaInput.ts @@ -1098,14 +1098,22 @@ async function addTypesInSchema( } let additionalProperties = schema.additionalProperties; - // This is an incorrect hack to fix an issue with a Go->Schema generator: + // This is an incorrect hack to treat a single pattern property as + // additional properties: // https://github.com/quicktype/quicktype/issues/976 + // https://github.com/quicktype/quicktype/issues/1464 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.fail.json b/test/inputs/schema/pattern-properties.1.fail.json new file mode 100644 index 0000000000..79d21bcfcf --- /dev/null +++ b/test/inputs/schema/pattern-properties.1.fail.json @@ -0,0 +1,3 @@ +{ + "enabled": {} +} diff --git a/test/inputs/schema/pattern-properties.1.json b/test/inputs/schema/pattern-properties.1.json new file mode 100644 index 0000000000..4e609c71b9 --- /dev/null +++ b/test/inputs/schema/pattern-properties.1.json @@ -0,0 +1,3 @@ +{ + "enabled": true +} diff --git a/test/inputs/schema/pattern-properties.schema b/test/inputs/schema/pattern-properties.schema new file mode 100644 index 0000000000..0aa1fd9aac --- /dev/null +++ b/test/inputs/schema/pattern-properties.schema @@ -0,0 +1,9 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "patternProperties": { + "^[a-zA-Z]\\w*$": { + "type": "boolean" + } + } +} From 82885d2c1c225e99817158260e33235a64cb4fb9 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 20:06:37 -0400 Subject: [PATCH 2/5] test(zod): skip top-level-map pattern-properties.schema The typescript-zod renderer emits no named schema export for a top-level map type, so the zod test driver throws 'No schema found'. This is a pre-existing zod limitation the new pattern-properties.schema fixture (a top-level map) exposes; the plain typescript renderer handles it fine. Scope the fixture out for zod. Co-Authored-By: Claude --- test/languages.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/languages.ts b/test/languages.ts index e268fae95f..eac4739977 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -1640,6 +1640,9 @@ export const TypeScriptZodLanguage: Language = { "multi-type-enum.schema", "keyword-unions.schema", "optional-any.schema", + // top-level map type produces no named schema export, so the zod + // driver can't find a TopLevelSchema to parse with + "pattern-properties.schema", "recursive-union-flattening.schema", "required.schema", "required-non-properties.schema", From 0ef0a19de7faf736842849eb32e02e4c56bb5f45 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 21:04:51 -0400 Subject: [PATCH 3/5] fix CI: skip pattern-properties.schema for cjson (#1464) cjson does not validate patternProperties/map value types against the input on parse (a known, documented limitation shared with go-schema-pattern-properties.schema), so the new negative test case never fails as expected. It also crashes (SIGSEGV) when printing a scalar (bool) map value produced from this schema, since the map/array item-printing code only special-cases string and object/union values. Skip the new fixture for cjson, matching the existing precedent for the same class of limitation, until that support is added. Co-Authored-By: gpt-5.6-sol via pi --- test/languages.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/languages.ts b/test/languages.ts index eac4739977..11b96377c3 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -547,6 +547,10 @@ export const CJSONLanguage: Language = { "go-schema-pattern-properties.schema", "multi-type-enum.schema", "nested-intersection-union.schema", + /* patternProperties value type is not validated on parse; also + hits an existing crash when a non-object/union scalar map value + is stored (tracked separately from this schema-inference fix) */ + "pattern-properties.schema", /* Constraints (min/max and regex) are not supported (for the current implementation, can be added later, should abord parsing and return NULL) */ "minmaxlength.schema", "optional-const-ref.schema", From 8842bb1d6224c9dcab879b0ea0ed17531318ff9c Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Tue, 21 Jul 2026 11:50:50 -0400 Subject: [PATCH 4/5] test(java): skip pattern-properties.schema (top-level map not validated) Java deserializes a top-level map via a raw `Map.class` reader (javaTypeWithoutGenerics drops the value type), so the Boolean map value type is not enforced on parse and pattern-properties.1.fail.json round-trips instead of failing. Skip the schema for Java; nested typed maps keep their value-validation coverage elsewhere, and the schema's positive and negative cases still run on other languages. Co-Authored-By: Claude --- test/languages.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/languages.ts b/test/languages.ts index 355b916130..2f0e26578f 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -244,6 +244,11 @@ export const JavaLanguage: Language = { skipSchema: [ "integer-before-number.schema", // Python-specific union-order regression. "keyword-unions.schema", // generates classes with names that are case-insensitively equal + // A top-level map deserializes via a raw `Map.class` reader + // (javaTypeWithoutGenerics drops the value type), so map value + // types are not validated on parse and the fail sample does not + // fail. Nested typed maps are still validated elsewhere. + "pattern-properties.schema", ], rendererOptions: {}, // The default is array-type=list; this keeps the T[] code path From 0e7f49c3ac43713456b9f7e59868f965b1908c8e Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Tue, 21 Jul 2026 12:14:24 -0400 Subject: [PATCH 5/5] test: skip top-level-map pattern-properties.schema where value types aren't enforced The new pattern-properties.schema is a top-level map whose fail sample `{"enabled": {}}` must be rejected. Languages that deserialize a map without validating the value type (unchecked generic casts, raw map readers, or no runtime type checks) round-trip it instead of failing, so add pattern-properties.schema to the shared skipsMapValueValidation list. That consolidates the previously per-language cjson and zod skips. PHP additionally can't run the positive case: it has no type aliases, so a top-level map produces no named TopLevel class and the driver's TopLevel::from() call fails. Skip it there for the same reason top-level unions, enums, and arrays are already skipped for PHP. Positive and negative cases still run on many validating languages (Go, Rust, Python, TypeScript, JavaScript, Flow, C#, C++, Scala, Elm, Dart, KotlinX, schema-to-schema). Co-Authored-By: Claude --- test/languages.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/languages.ts b/test/languages.ts index 2f0e26578f..1d730bf8e4 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -54,6 +54,10 @@ const skipsUntypedUnions = [ const skipsMapValueValidation = [ "go-schema-pattern-properties.schema", "unevaluated-properties.schema", + // These languages deserialize a map without validating the value type + // (unchecked generic casts, raw map readers, or no runtime type checks), + // so the `{"enabled": {}}` fail sample round-trips instead of failing. + "pattern-properties.schema", ]; export type LanguageFeature = @@ -640,10 +644,6 @@ export const CJSONLanguage: Language = { ...skipsMapValueValidation, "multi-type-enum.schema", "nested-intersection-union.schema", - /* patternProperties value type is not validated on parse; also - hits an existing crash when a non-object/union scalar map value - is stored (tracked separately from this schema-inference fix) */ - "pattern-properties.schema", "prefix-items.schema", /* Constraints (min/max and regex) are not supported (for the current implementation, can be added later, should abord parsing and return NULL) */ "minmaxlength.schema", @@ -1960,6 +1960,9 @@ export const PHPLanguage: Language = { "top-level-enum.schema", // The driver does not support top-level arrays. "union.schema", + // PHP has no type aliases, so a top-level map produces no named + // TopLevel class and the driver's TopLevel::from() call fails. + "pattern-properties.schema", ], rendererOptions: {}, quickTestRendererOptions: [], @@ -2072,9 +2075,6 @@ export const TypeScriptZodLanguage: Language = { "multi-type-enum.schema", "keyword-unions.schema", "optional-any.schema", - // top-level map type produces no named schema export, so the zod - // driver can't find a TopLevelSchema to parse with - "pattern-properties.schema", "recursive-union-flattening.schema", "required.schema", "required-non-properties.schema",