diff --git a/packages/quicktype-core/src/rewrites/ResolveIntersections.ts b/packages/quicktype-core/src/rewrites/ResolveIntersections.ts index 05b424f43..12aea46c2 100644 --- a/packages/quicktype-core/src/rewrites/ResolveIntersections.ts +++ b/packages/quicktype-core/src/rewrites/ResolveIntersections.ts @@ -183,6 +183,9 @@ class IntersectionAccumulator return; } + // Named properties from intersected objects are combined. Whether + // additional properties are allowed only affects names not declared + // by any of those objects. const allPropertyNames = setUnionInto( new Set(this._objectProperties.keys()), maybeObject.getProperties().keys(), diff --git a/test/inputs/schema/allof-closed-objects.1.fail.no-defaults.json b/test/inputs/schema/allof-closed-objects.1.fail.no-defaults.json new file mode 100644 index 000000000..a145c7895 --- /dev/null +++ b/test/inputs/schema/allof-closed-objects.1.fail.no-defaults.json @@ -0,0 +1,6 @@ +{ + "animal": { + "foo": "inherited property", + "bar": "inline property" + } +} diff --git a/test/inputs/schema/allof-closed-objects.1.json b/test/inputs/schema/allof-closed-objects.1.json new file mode 100644 index 000000000..a5e433dd2 --- /dev/null +++ b/test/inputs/schema/allof-closed-objects.1.json @@ -0,0 +1,7 @@ +{ + "animal": { + "foo": "inherited property", + "discriminator": "dog", + "bar": "inline property" + } +} diff --git a/test/inputs/schema/allof-closed-objects.schema b/test/inputs/schema/allof-closed-objects.schema new file mode 100644 index 000000000..35727a276 --- /dev/null +++ b/test/inputs/schema/allof-closed-objects.schema @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Container", + "type": "object", + "additionalProperties": false, + "properties": { + "animal": { + "oneOf": [ + { "$ref": "#/definitions/dog" }, + { "type": "null" } + ] + } + }, + "definitions": { + "dog": { + "allOf": [ + { "$ref": "#/definitions/animal" }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "bar": { "type": ["null", "string"] } + } + } + ] + }, + "animal": { + "type": "object", + "additionalProperties": false, + "required": ["discriminator"], + "properties": { + "foo": { "type": ["null", "string"] }, + "discriminator": { "type": "string" } + } + } + } +} diff --git a/test/languages.ts b/test/languages.ts index 120f3170b..1a68ffd4f 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -678,6 +678,7 @@ export const CJSONLanguage: Language = { /* Required properties absent are not checked (for the current implementation, can be added later, should abord parsing and return NULL) */ "intersection.schema", "required.schema", + "allof-closed-objects.schema", // The default-value fail sample also relies on required-property // enforcement, which cJSON does not do. "default-value.schema", @@ -1002,6 +1003,8 @@ export const SwiftLanguage: Language = { // This works on macOS, but on Linux one of the failure test cases doesn't fail ...skipsUntypedUnions, "required.schema", + // Same Linux-only missing-required behavior as required.schema. + "allof-closed-objects.schema", // The default-value fail sample also relies on required-property enforcement. "default-value.schema", "multi-type-enum.schema", @@ -1918,6 +1921,9 @@ export const HaskellLanguage: Language = { "keyword-unions.schema", "optional-any.schema", "required.schema", + // Same undetectable expected-failure limitation as required.schema: + // a failed decode prints "null" and exits 0. + "allof-closed-objects.schema", // The default-value fail sample also relies on required-property enforcement. "default-value.schema", "required-non-properties.schema", @@ -2273,6 +2279,7 @@ export const ElixirLanguage: Language = { // Struct keys cannot be enforced at runtime in Elixir and their values will just be set to null. "strict-optional.schema", "required.schema", + "allof-closed-objects.schema", // The default-value fail sample also relies on required-property enforcement. "default-value.schema", "boolean-subschema.schema",