diff --git a/src/generators/jsonSchema.js b/src/generators/jsonSchema.js index f1b5e39..8d42c5b 100644 --- a/src/generators/jsonSchema.js +++ b/src/generators/jsonSchema.js @@ -1,6 +1,6 @@ import wu from 'wu'; -import {invariant, collect} from '../utils'; +import {invariant, collect, partition} from '../utils'; import type Fund from '../fund'; import type {Type, NumberType} from '../types'; @@ -114,8 +114,29 @@ function convert(fund: Fund, type: ?Type): Schema { anyOf: schemas, }; case 'intersection': - return { - allOf: wu(type.parts).map(part => convert(fund, part)).toArray(), + const [maps, others] = partition(type.parts, type => type.kind === 'map'); + + const parts = wu(others).map(part => convert(fund, part)).toArray(); + + if (maps.length > 0) { + const keys = wu(maps).map(map => convert(fund, (map: $FlowFixMe).values)).toArray(); + const key = keys.length === 1 ? keys[0] : {anyOf: keys}; + + if (parts.length === 1 && parts[0].type === 'object') { + invariant(typeof parts[0] === 'object'); + invariant(parts[0].additionalProperties == null); + + parts[0].additionalProperties = key; + } else { + parts.push({ + type: 'object', + additionalProperties: key, + }); + } + } + + return parts.length === 1 ? parts[0] : { + allOf: parts, }; case 'maybe': return { diff --git a/src/utils.js b/src/utils.js index 2de1caf..01c9374 100644 --- a/src/utils.js +++ b/src/utils.js @@ -19,3 +19,13 @@ export function collect(iter: Iterable<[string, T]>): {[string]: T} { return result; } + +export function partition(iter: Iterable, pred: T => boolean): [T[], T[]] { + const [left, right] = [[], []]; + + for (const item of iter) { + (pred(item) ? left : right).push(item); + } + + return [left, right]; +} diff --git a/tests/samples/maps/schema.json b/tests/samples/maps/schema.json index 32cf48a..624a43c 100644 --- a/tests/samples/maps/schema.json +++ b/tests/samples/maps/schema.json @@ -10,20 +10,19 @@ "additionalProperties": {"type": "boolean"} }, "maps::Couple": { - "allOf": [ - {"type": "object", "additionalProperties": {"type": "boolean"}}, - {"type": "object", "additionalProperties": {"type": "string"}} - ] + "type": "object", + "additionalProperties": { + "anyOf": [ + {"type": "boolean"}, + {"type": "string"} + ] + } }, "maps::Mix": { - "allOf": [ - { - "type": "object", - "properties": {"foo": {"type": "string"}}, - "required": ["foo"] - }, - {"type": "object", "additionalProperties": {"type": "boolean"}} - ] + "type": "object", + "properties": {"foo": {"type": "string"}}, + "required": ["foo"], + "additionalProperties": {"type": "boolean"} } } }