Skip to content

fix(schema): keep allOf-merged properties when additionalProperties: false#3058

Merged
schani merged 2 commits into
masterfrom
agent/fix-issue-1257
Jul 21, 2026
Merged

fix(schema): keep allOf-merged properties when additionalProperties: false#3058
schani merged 2 commits into
masterfrom
agent/fix-issue-1257

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

Bug

Issue #1257: when a JSON Schema allOf merges branches that each set
additionalProperties: false (a very common pattern, e.g. one branch is a
$ref to a shared base object and the other is inline), quicktype's
TypeScript output dropped all the properties instead of merging them.

Repro:

{
  "definitions": {
    "AmountAndFrequency": {
      "type": "object",
      "properties": {
        "amount": { "type": "number" },
        "frequency": { "type": "string", "enum": ["Weekly", "Monthly", "Annually"] }
      },
      "additionalProperties": false,
      "required": ["amount", "frequency"]
    },
    "Income": {
      "allOf": [
        { "$ref": "#/definitions/AmountAndFrequency" },
        {
          "type": "object",
          "properties": {
            "type": { "type": "string", "enum": ["GrossSalaryAmount", "Overtime"] },
            "description": { "type": "string" }
          },
          "additionalProperties": false,
          "required": ["type"]
        }
      ]
    }
  },
  "$ref": "#/definitions/Income"
}
node dist/index.js --src schema.json --src-lang schema --lang ts

Before:

export interface Schema {
}

After:

export interface Schema {
    amount:       number;
    frequency:    Frequency;
    description?: string;
    type:         Type;
}

Root cause

IntersectionAccumulator.updateObjectProperties in
packages/quicktype-core/src/rewrites/ResolveIntersections.ts merges the
object types being intersected by an allOf. For each property name seen so
far across branches, if the current branch didn't declare that property and
didn't allow additional properties, the property was deleted from the merged
result — even though some other branch did declare it. Since JSON Schema
input in the wild very commonly sets additionalProperties: false on every
branch, this deleted essentially all disjoint properties, frequently leaving
an empty merged object.

Fix

A property known from any allOf branch is now kept in the merged object
regardless of whether another branch restricts additional properties;
additionalProperties: false still correctly disallows properties that no
branch declares (that behavior is untouched — only the erroneous deletion of
already-known properties was removed).

Test coverage

Added test/inputs/schema/all-of-additional-properties-false.schema
(the repro above) with:

  • .1.json: a positive round-trip sample with all four merged properties.
  • .1.fail.enum.json: a negative sample with an invalid enum value for
    frequency, a property that's only present at all because the merge now
    works — it guards against a regression of this exact bug rather than
    testing something unrelated.

Verification

  • npm run build passes.
  • npm run test:unit passes (176 tests).
  • QUICKTEST=true FIXTURE=schema-typescript script/test passes (exit 0),
    including the new fixture (both the positive and .fail.enum samples).
  • Manually re-ran the repro above before/after the fix as shown.
  • The other schema-<language> fixtures were not run locally due to
    time/toolchain constraints; CI will validate them.

Fixes #1257

🤖 Generated with Claude Code

schani and others added 2 commits July 20, 2026 19:19
…alProperties: false (#1257)

When merging object types from an `allOf`, IntersectionAccumulator
discarded a property as soon as any other branch didn't declare that
property and didn't allow additional properties. Since JSON Schema
input commonly sets `additionalProperties: false` on every branch,
this meant merging two or more `allOf` branches with disjoint property
sets produced an empty (or partially empty) merged object instead of
the union of all their properties.

Fix ResolveIntersections.ts so a property known from any branch is
kept in the merged object regardless of whether other branches
restrict additional properties; `additionalProperties: false` still
correctly disallows properties nobody declared.

Added test/inputs/schema/all-of-additional-properties-false.schema
with a positive fixture sample and a `.fail.enum` negative sample
(invalid enum value for a property merged in from one of the allOf
branches, which only fails if that property survived the merge).

Verified locally: npm run build passes, npm run test:unit passes (176
tests), and QUICKTEST=true FIXTURE=schema-typescript script/test
passes (exit 0), including the new fixture. CI will cover the other
schema-<language> fixtures.

Fixes #1257

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
cjson's generated deserializer does not validate enum values, so the
`.1.fail.enum.json` negative sample of the new
`all-of-additional-properties-false.schema` fixture round-trips instead of
being rejected, making the expected-failure assertion fail. Add the schema
to the shared `skipsEnumValueValidation` list, matching the existing enum
fixtures that other non-enum-validating languages already skip.

Co-Authored-By: Claude <noreply@anthropic.com>
@schani
schani merged commit bbc3134 into master Jul 21, 2026
28 checks passed
@schani
schani deleted the agent/fix-issue-1257 branch July 21, 2026 00:25
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.

allOf in schema is not merged in Typescript output

1 participant