diff --git a/src/NodeParser/MappedTypeNodeParser.ts b/src/NodeParser/MappedTypeNodeParser.ts index baa494f79..dc03950b7 100644 --- a/src/NodeParser/MappedTypeNodeParser.ts +++ b/src/NodeParser/MappedTypeNodeParser.ts @@ -65,7 +65,7 @@ export class MappedTypeNodeParser implements SubNodeParser { return type instanceof NeverType ? new NeverType() : new ArrayType(type); } // Key type widens to `string` - const type = this.childNodeParser.createType(node.type!, context); + const type = this.childNodeParser.createType(node.type!, this.createSubContext(node, keyListType, context)); // const resultType = type instanceof NeverType ? new NeverType() : new ObjectType(id, [], [], type); const resultType = new ObjectType(id, [], [], type); if (resultType) { @@ -162,13 +162,10 @@ export class MappedTypeNodeParser implements SubNodeParser { return this.additionalProperties; } - const key = keyListType.getTypes().filter((type) => !(derefType(type) instanceof LiteralType))[0]; + const nonLiteral = keyListType.getTypes().find((type) => !(derefType(type) instanceof LiteralType)); - if (key) { - return ( - this.childNodeParser.createType(node.type!, this.createSubContext(node, key, context)) ?? - this.additionalProperties - ); + if (nonLiteral) { + return this.childNodeParser.createType(node.type!, this.createSubContext(node, nonLiteral, context)); } return this.additionalProperties; diff --git a/src/NodeParser/TypeOperatorNodeParser.ts b/src/NodeParser/TypeOperatorNodeParser.ts index 5e4353d36..84d805483 100644 --- a/src/NodeParser/TypeOperatorNodeParser.ts +++ b/src/NodeParser/TypeOperatorNodeParser.ts @@ -2,7 +2,7 @@ import ts from "typescript"; import type { Context, NodeParser } from "../NodeParser.js"; import type { SubNodeParser } from "../SubNodeParser.js"; import { ArrayType } from "../Type/ArrayType.js"; -import type { BaseType } from "../Type/BaseType.js"; +import { BaseType } from "../Type/BaseType.js"; import { NumberType } from "../Type/NumberType.js"; import { ObjectType } from "../Type/ObjectType.js"; import { StringType } from "../Type/StringType.js"; @@ -28,7 +28,7 @@ export class TypeOperatorNodeParser implements SubNodeParser { return new NumberType(); } const keys = getTypeKeys(type); - if (derefed instanceof ObjectType && derefed.getAdditionalProperties()) { + if (derefed instanceof ObjectType && derefed.getAdditionalProperties() instanceof BaseType) { return new UnionType([...keys, new StringType()]); } diff --git a/test/config.test.ts b/test/config.test.ts index 9ed4fdb84..39aba6853 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -406,6 +406,22 @@ describe("config", () => { }), ); + it( + "mapped-intersection-index", + assertSchema("mapped-intersection-index", { + type: "MyObject", + additionalProperties: true, + }), + ); + + it( + "mapped-index-any", + assertSchema("mapped-index-any", { + type: "*", + additionalProperties: true, + }), + ); + it( "arrow-function-parameters", assertSchema("arrow-function-parameters", { diff --git a/test/config/mapped-index-any/main.ts b/test/config/mapped-index-any/main.ts new file mode 100644 index 000000000..95447b651 --- /dev/null +++ b/test/config/mapped-index-any/main.ts @@ -0,0 +1,15 @@ +interface BaseA { + foo: string; + [key: string]: any; +} + +type Keep = { [K in keyof B]: B[K] }; + +export type MyObjectA = Keep; + +interface BaseB { + foo: string; + [key: string]: unknown; +} + +export type MyObjectB = { [K in keyof BaseB]: any }; diff --git a/test/config/mapped-index-any/schema.json b/test/config/mapped-index-any/schema.json new file mode 100644 index 000000000..e4fd7bdee --- /dev/null +++ b/test/config/mapped-index-any/schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "MyObjectA": { + "properties": { + "foo": { + "type": "string" + } + }, + "required": [ + "foo" + ], + "type": "object" + }, + "MyObjectB": { + "properties": { + "foo": {} + }, + "required": [ + "foo" + ], + "type": "object" + } + } +} diff --git a/test/config/mapped-intersection-complex/schema.json b/test/config/mapped-intersection-complex/schema.json new file mode 100644 index 000000000..ec68db745 --- /dev/null +++ b/test/config/mapped-intersection-complex/schema.json @@ -0,0 +1,21 @@ +{ + "$ref": "#/definitions/MyObject", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "MyObject": { + "properties": { + "bar": { + "type": "number" + }, + "foo": { + "type": "string" + } + }, + "required": [ + "bar", + "foo" + ], + "type": "object" + } + } +} diff --git a/test/config/mapped-intersection-index/main.ts b/test/config/mapped-intersection-index/main.ts new file mode 100644 index 000000000..378a232e7 --- /dev/null +++ b/test/config/mapped-intersection-index/main.ts @@ -0,0 +1,11 @@ +interface Base { + foo: string; + bar: null; + [key: string]: any; +} + +type Keep = { [P in Exclude]: B[P] }; + +type Override>> = Keep & O; + +export type MyObject = Override; diff --git a/test/config/mapped-intersection-index/schema.json b/test/config/mapped-intersection-index/schema.json new file mode 100644 index 000000000..ec68db745 --- /dev/null +++ b/test/config/mapped-intersection-index/schema.json @@ -0,0 +1,21 @@ +{ + "$ref": "#/definitions/MyObject", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "MyObject": { + "properties": { + "bar": { + "type": "number" + }, + "foo": { + "type": "string" + } + }, + "required": [ + "bar", + "foo" + ], + "type": "object" + } + } +}