Skip to content

fix(schema): use patternProperties as map value type#3024

Open
schani wants to merge 8 commits into
masterfrom
agent/fix-issue-1549
Open

fix(schema): use patternProperties as map value type#3024
schani wants to merge 8 commits into
masterfrom
agent/fix-issue-1549

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

Bug

An object schema with additionalProperties: false (or unset) plus
patternProperties was ignored entirely, so quicktype generated an empty
interface/class
for that object instead of a map keyed by the pattern's
value type.

Minimal repro (from the issue, --src-lang schema --lang ts):

"a2": {
  "type": "object",
  "additionalProperties": false,
  "patternProperties": { "^.+$": { "type": "string" } }
}

Before:

export interface Schema {
    a2?: A2;
}
export interface A2 {
}

After:

export interface Schema {
    a2?: { [key: string]: string };
}

Same fix applies to C# (a2 now generates Dictionary<string, string>
instead of an empty class).

Root cause

packages/quicktype-core/src/input/JSONSchemaInput.ts only ever looked at
patternProperties["*"] (a narrow hack originally added for issue #976) and
only when additionalProperties was undefined. Any other pattern key, or
an explicit additionalProperties: false, meant patternProperties was
silently dropped, producing an object with no properties and no map type.

Fix

When additionalProperties is not true (quicktype can't represent
per-pattern keys, so it approximates all matching properties with a map
type), collect the schemas of all patternProperties entries — plus any
schema-valued additionalProperties — and use them as the object's
additional-properties (map value) type; combine multiple schemas with
anyOf. Also added schema.patternProperties !== undefined to the set of
schema fields that mark a JSON Schema node as needing type expansion, so
patternProperties-only nodes aren't skipped.

Test coverage

Added an end-to-end JSON Schema fixture:

  • test/inputs/schema/pattern-properties-map.schema
  • test/inputs/schema/pattern-properties-map.1.json

reproducing the issue's a1/a2 example (additionalProperties: true and
additionalProperties: false, both with patternProperties). This is
auto-discovered by the existing schema-fixture harness across all
languages that run schema fixtures.

Verification

  • npm run build passes.
  • npx vitest run test/unit — 169/169 passing.
  • FIXTURE=schema-typescript script/test — 71/71 passing, including the
    new pattern-properties-map.schema fixture and the pre-existing
    go-schema-pattern-properties.schema fixture (confirming issue Infer property type based on patternProperties if pattern is .* #976's
    original scenario still works).
  • Manually re-ran the issue's repro with --lang ts and --lang cs and
    confirmed both now emit a map type instead of an empty
    interface/class.
  • schema-csharp fixture was not run locally (no dotnet toolchain in
    this environment); CI will validate it.

Fixes #1549

🤖 Generated with Claude Code

schani and others added 7 commits July 20, 2026 17:53
Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
)

Co-Authored-By: Claude <noreply@anthropic.com>
…ed map)

The pattern-properties-map.schema fixture's a1 uses additionalProperties:
true, which quicktype models as a map with an any-typed value. cjson does
not support the pure Any type (see the existing any.schema / optional-any.schema
skips), so it drops arbitrary map entries on round-trip. Scope the fixture out
for cjson, matching the existing documented limitation.

Co-Authored-By: Claude <noreply@anthropic.com>
…imitation (#3024)

Kotlin (Klaxon and Jackson) coerces the wrong-typed map value in
pattern-properties-map.1.fail.json's "team": 42 instead of rejecting it,
so the fail sample doesn't fail as expected. Add the schema to the
existing skipsMapValueValidation list, the same documented remedy already
used for go-schema-pattern-properties.schema and unevaluated-properties.schema.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
… backends

The bare `.fail.json` sample put a number where a string-typed map value was
expected. Many backends (Java/Jackson, C#, Kotlin, Rust, Python, etc.)
leniently coerce a JSON number into a string when deserializing a
`map<string, string>`, so the sample did not make the generated program exit
nonzero and the "Expected failure" assertion failed across those languages.

Mirror the proven `go-schema-pattern-properties` design instead: add an
integer-typed pattern-properties map (`a3`) and drive the negative case with a
string where an integer is expected. Non-numeric-to-integer coercion is
reliably rejected by every backend that validates map values, and the few that
do not validate map values at all already skip this schema via
`skipsMapValueValidation`. The faithful `a1` (any-valued) and `a2`
(string-valued) maps from issue #1549 are kept as-is.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Generated-output differences

23 files differ — 0 modified, 23 new, 0 deleted
2019 changed lines — +2019 / −0

Open the generated-output report →

@github-actions

Copy link
Copy Markdown

Generated-output differences

24 files differ — 0 modified, 24 new, 0 deleted
2086 changed lines — +2086 / −0

Open the generated-output report →

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

optional fields missing when input file is json schema (to typescript & c# tested)

1 participant