This isn't a GREAT answer. But until the definition of JSONSchema is improved (or someone provides a better answer) - this is what I've come up with as workable.
Basically, you define two copies of each type, the first with all the details but no additionalProperties: false flag. Then second, REFERENCING the first, but with the 'additionalProperties: false' set.
The first you can think of as an 'abstract class' and the second as a 'concrete class'.
Then, to 'subclass', you use the https://json-schema.org/understanding-json-schema/structuring.html#extending approach, but referencing the ABSTRACT class, and then add the 'additionalProperties: false'. SADLY, to make this work, you must also REPEAT all the inherited properties (but no need to include their type info - just their names) - due to the sad choice for how JSONSchema draft 7 appears to interpret additionalProperties.
An EXAMPLE - based on https://json-schema.org/understanding-json-schema/structuring.html#extending should help:
https://www.jsonschemavalidator.net/s/3fhU3O1X
(reproduced here in case other site
/link not permanant/reliable)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://TEST",
"definitions": {
"interface-address": {
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
}
},
"required": ["street_address", "city", "state"]
},
"concrete-address": {
"allOf": [
{
"$ref": "#/definitions/interface-address"
}
],
"properties": {
"street_address": {},
"city": {},
"state": {}
},
"additionalProperties": false
},
"in-another-file-subclass-address": {
"allOf": [
{
"$ref": "#/definitions/interface-address"
}
],
"additionalProperties": false,
"properties": {
"street_address": {},
"city": {},
"state": {},
"type": {
"enum": ["residential", "business"]
}
},
"required": ["type"]
},
"test-of-address-schemas": {
"type": "object",
"properties": {
"interface-address-allows-bad-fields": {
"$ref": "#/definitions/interface-address"
},
"use-concrete-address-to-only-admit-legit-addresses-without-extra-crap": {
"$ref": "#/definitions/concrete-address"
},
"still-can-subclass-using-interface-not-concrete": {
"$ref": "#/definitions/in-another-file-subclass-address"
}
}
}
},
"anyOf": [
{
"$ref": "#/definitions/test-of-address-schemas"
}
]
}
and example document:
{
"interface-address-allows-bad-fields":{
"street_address":"s",
"city":"s",
"state":"s",
"allow-bad-fields-this-is-why-we-need-additionalProperties":"s"
},
"use-concrete-address-to-only-admit-legit-addresses-without-extra-crap":{
"street_address":"s",
"city":"s",
"state":"s"
},
"still-can-subclass-using-interface-not-concrete":{
"street_address":"s",
"city":"s",
"state":"s",
"type":"business"
}
}
unevaluatedProperties(and$recursiveRef) were added. - Ether