From 1b0863c09f0a3c4cc14bb381806869c3413aac94 Mon Sep 17 00:00:00 2001 From: Filipe Pomar Date: Sun, 1 May 2022 22:10:18 +0200 Subject: [PATCH 1/4] feat: adding support for intersection of arrays and tuples --- .../IntersectionTypeFormatter.ts | 40 ++++++++++++++++--- test/valid-data-type.test.ts | 1 + .../type-intersection-with-arrays/main.ts | 5 +++ .../type-intersection-with-arrays/schema.json | 39 ++++++++++++++++++ 4 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 test/valid-data/type-intersection-with-arrays/main.ts create mode 100644 test/valid-data/type-intersection-with-arrays/schema.json diff --git a/src/TypeFormatter/IntersectionTypeFormatter.ts b/src/TypeFormatter/IntersectionTypeFormatter.ts index 6f19d7e4..4d677cf8 100644 --- a/src/TypeFormatter/IntersectionTypeFormatter.ts +++ b/src/TypeFormatter/IntersectionTypeFormatter.ts @@ -1,7 +1,9 @@ import { Definition } from "../Schema/Definition"; import { SubTypeFormatter } from "../SubTypeFormatter"; +import { ArrayType } from "../Type/ArrayType"; import { BaseType } from "../Type/BaseType"; import { IntersectionType } from "../Type/IntersectionType"; +import { TupleType } from "../Type/TupleType"; import { TypeFormatter } from "../TypeFormatter"; import { getAllOfDefinitionReducer } from "../Utils/allOfDefinition"; import { uniqueArray } from "../Utils/uniqueArray"; @@ -16,12 +18,38 @@ export class IntersectionTypeFormatter implements SubTypeFormatter { public getDefinition(type: IntersectionType): Definition { const types = type.getTypes(); - return types.length > 1 - ? types.reduce(getAllOfDefinitionReducer(this.childTypeFormatter), { - type: "object", - additionalProperties: false, - } as Definition) - : this.childTypeFormatter.getDefinition(types[0]); + if (types.length <= 1) { + return this.childTypeFormatter.getDefinition(types[0]); + } + + const requirements: Definition[] = []; + const otherTypes: BaseType[] = []; + + types.forEach((t) => { + if (t instanceof ArrayType || t instanceof TupleType) { + /** + * Arrays are not easily mergeable + * So it's just easier to append their defs + */ + requirements.push(this.childTypeFormatter.getDefinition(t)); + } else { + otherTypes.push(t); + } + }); + + if (otherTypes.length) { + /** + * There are non array (mergeable requirements) + */ + requirements.push( + otherTypes.reduce(getAllOfDefinitionReducer(this.childTypeFormatter), { + type: "object", + additionalProperties: false, + }) + ); + } + + return requirements.length === 1 ? requirements[0] : { allOf: requirements }; } public getChildren(type: IntersectionType): BaseType[] { diff --git a/test/valid-data-type.test.ts b/test/valid-data-type.test.ts index a05bc9c3..596e3cc6 100644 --- a/test/valid-data-type.test.ts +++ b/test/valid-data-type.test.ts @@ -36,6 +36,7 @@ describe("valid-data-type", () => { it("type-union", assertValidSchema("type-union", "TypeUnion")); it("type-union-tagged", assertValidSchema("type-union-tagged", "Shape")); it("type-intersection", assertValidSchema("type-intersection", "MyObject")); + it("type-intersection-with-arrays", assertValidSchema("type-intersection-with-arrays", "*")); it("type-intersection-conflict", assertValidSchema("type-intersection-conflict", "MyObject")); it("type-intersection-partial-conflict", assertValidSchema("type-intersection-partial-conflict", "MyType")); it("type-intersection-partial-conflict-ref", assertValidSchema("type-intersection-partial-conflict", "MyType")); diff --git a/test/valid-data/type-intersection-with-arrays/main.ts b/test/valid-data/type-intersection-with-arrays/main.ts new file mode 100644 index 00000000..78ec504e --- /dev/null +++ b/test/valid-data/type-intersection-with-arrays/main.ts @@ -0,0 +1,5 @@ +export type NonEmptyNumberArrayVariationOne = [number, ...number[]] & number[]; + +type NonEmptyGenericArrayVariationOne = [T, ...T[]] & T[]; + +export type NonEmptyNumberArrayVariationTwo = NonEmptyGenericArrayVariationOne; diff --git a/test/valid-data/type-intersection-with-arrays/schema.json b/test/valid-data/type-intersection-with-arrays/schema.json new file mode 100644 index 00000000..47f8a368 --- /dev/null +++ b/test/valid-data/type-intersection-with-arrays/schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "NonEmptyNumberArrayVariationOne": { + "allOf": [ + { + "items": { + "type": "number" + }, + "minItems": 1, + "type": "array" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ] + }, + "NonEmptyNumberArrayVariationTwo": { + "allOf": [ + { + "items": { + "type": "number" + }, + "minItems": 1, + "type": "array" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ] + } + } +} From bc6f45bb298e070d18fb9fd67a3d4f00b01071f5 Mon Sep 17 00:00:00 2001 From: Filipe Pomar Date: Mon, 2 May 2022 09:08:54 +0200 Subject: [PATCH 2/4] refactor: updating variable names for PR --- src/TypeFormatter/IntersectionTypeFormatter.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/TypeFormatter/IntersectionTypeFormatter.ts b/src/TypeFormatter/IntersectionTypeFormatter.ts index 4d677cf8..26975b1b 100644 --- a/src/TypeFormatter/IntersectionTypeFormatter.ts +++ b/src/TypeFormatter/IntersectionTypeFormatter.ts @@ -22,8 +22,8 @@ export class IntersectionTypeFormatter implements SubTypeFormatter { return this.childTypeFormatter.getDefinition(types[0]); } - const requirements: Definition[] = []; - const otherTypes: BaseType[] = []; + const dependencies: Definition[] = []; + const nonArrayLikeTypes: BaseType[] = []; types.forEach((t) => { if (t instanceof ArrayType || t instanceof TupleType) { @@ -31,25 +31,25 @@ export class IntersectionTypeFormatter implements SubTypeFormatter { * Arrays are not easily mergeable * So it's just easier to append their defs */ - requirements.push(this.childTypeFormatter.getDefinition(t)); + dependencies.push(this.childTypeFormatter.getDefinition(t)); } else { - otherTypes.push(t); + nonArrayLikeTypes.push(t); } }); - if (otherTypes.length) { + if (nonArrayLikeTypes.length) { /** * There are non array (mergeable requirements) */ - requirements.push( - otherTypes.reduce(getAllOfDefinitionReducer(this.childTypeFormatter), { + dependencies.push( + nonArrayLikeTypes.reduce(getAllOfDefinitionReducer(this.childTypeFormatter), { type: "object", additionalProperties: false, }) ); } - return requirements.length === 1 ? requirements[0] : { allOf: requirements }; + return dependencies.length === 1 ? dependencies[0] : { allOf: dependencies }; } public getChildren(type: IntersectionType): BaseType[] { From 40b6cea8266fa401084c9cdc779263757bbf7f48 Mon Sep 17 00:00:00 2001 From: Filipe Pomar Date: Mon, 2 May 2022 10:21:41 +0200 Subject: [PATCH 3/4] refactor: remove functionally useless branches --- src/TypeFormatter/IntersectionTypeFormatter.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/TypeFormatter/IntersectionTypeFormatter.ts b/src/TypeFormatter/IntersectionTypeFormatter.ts index 26975b1b..ba5085d4 100644 --- a/src/TypeFormatter/IntersectionTypeFormatter.ts +++ b/src/TypeFormatter/IntersectionTypeFormatter.ts @@ -18,10 +18,6 @@ export class IntersectionTypeFormatter implements SubTypeFormatter { public getDefinition(type: IntersectionType): Definition { const types = type.getTypes(); - if (types.length <= 1) { - return this.childTypeFormatter.getDefinition(types[0]); - } - const dependencies: Definition[] = []; const nonArrayLikeTypes: BaseType[] = []; From 2c2e655884452bb4edb381268bbb2072fa1229b4 Mon Sep 17 00:00:00 2001 From: Filipe Pomar Date: Tue, 3 May 2022 18:18:00 +0200 Subject: [PATCH 4/4] style: fix coding style --- src/TypeFormatter/IntersectionTypeFormatter.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/TypeFormatter/IntersectionTypeFormatter.ts b/src/TypeFormatter/IntersectionTypeFormatter.ts index ba5085d4..7530499f 100644 --- a/src/TypeFormatter/IntersectionTypeFormatter.ts +++ b/src/TypeFormatter/IntersectionTypeFormatter.ts @@ -16,27 +16,21 @@ export class IntersectionTypeFormatter implements SubTypeFormatter { } public getDefinition(type: IntersectionType): Definition { - const types = type.getTypes(); - const dependencies: Definition[] = []; const nonArrayLikeTypes: BaseType[] = []; - types.forEach((t) => { + for (const t of type.getTypes()) { + // Filter out Array like definitions that cannot be + // easily mergeable into a single json-schema object if (t instanceof ArrayType || t instanceof TupleType) { - /** - * Arrays are not easily mergeable - * So it's just easier to append their defs - */ dependencies.push(this.childTypeFormatter.getDefinition(t)); } else { nonArrayLikeTypes.push(t); } - }); + } if (nonArrayLikeTypes.length) { - /** - * There are non array (mergeable requirements) - */ + // There are non array (mergeable requirements) dependencies.push( nonArrayLikeTypes.reduce(getAllOfDefinitionReducer(this.childTypeFormatter), { type: "object",