diff --git a/factory/formatter.ts b/factory/formatter.ts index 11fb516c2..5bf8753c7 100644 --- a/factory/formatter.ts +++ b/factory/formatter.ts @@ -27,6 +27,7 @@ import { UnionTypeFormatter } from "../src/TypeFormatter/UnionTypeFormatter"; import { UnknownTypeFormatter } from "../src/TypeFormatter/UnknownTypeFormatter"; import { VoidTypeFormatter } from "../src/TypeFormatter/VoidTypeFormatter"; import { MutableTypeFormatter } from "../src/MutableTypeFormatter"; +import { NeverTypeFormatter } from "../src/TypeFormatter/NeverTypeFormatter"; export type FormatterAugmentor = ( formatter: MutableTypeFormatter, @@ -54,6 +55,7 @@ export function createFormatter(config: Config, augmentor?: FormatterAugmentor): .addTypeFormatter(new UndefinedTypeFormatter()) .addTypeFormatter(new UnknownTypeFormatter()) .addTypeFormatter(new VoidTypeFormatter()) + .addTypeFormatter(new NeverTypeFormatter()) .addTypeFormatter(new LiteralTypeFormatter()) .addTypeFormatter(new EnumTypeFormatter()) diff --git a/src/NodeParser/NeverTypeNodeParser.ts b/src/NodeParser/NeverTypeNodeParser.ts index c2632f110..364738b0e 100644 --- a/src/NodeParser/NeverTypeNodeParser.ts +++ b/src/NodeParser/NeverTypeNodeParser.ts @@ -2,12 +2,13 @@ import ts from "typescript"; import { Context } from "../NodeParser"; import { SubNodeParser } from "../SubNodeParser"; import { BaseType } from "../Type/BaseType"; +import { NeverType } from "../Type/NeverType"; export class NeverTypeNodeParser implements SubNodeParser { public supportsNode(node: ts.KeywordTypeNode): boolean { return node.kind === ts.SyntaxKind.NeverKeyword; } public createType(node: ts.KeywordTypeNode, context: Context): BaseType | undefined { - return undefined; + return new NeverType(); } } diff --git a/src/Type/NeverType.ts b/src/Type/NeverType.ts new file mode 100644 index 000000000..f2f8c7e78 --- /dev/null +++ b/src/Type/NeverType.ts @@ -0,0 +1,7 @@ +import { BaseType } from "./BaseType"; + +export class NeverType extends BaseType { + public getId(): string { + return "never"; + } +} diff --git a/src/Type/UnionType.ts b/src/Type/UnionType.ts index 34c2d1d16..9cd0f8cb8 100644 --- a/src/Type/UnionType.ts +++ b/src/Type/UnionType.ts @@ -1,5 +1,6 @@ import { BaseType } from "./BaseType"; import { uniqueTypeArray } from "../Utils/uniqueTypeArray"; +import { NeverType } from "./NeverType"; export class UnionType extends BaseType { private readonly types: BaseType[]; @@ -10,7 +11,7 @@ export class UnionType extends BaseType { types.reduce((flatTypes, type) => { if (type instanceof UnionType) { flatTypes.push(...type.getTypes()); - } else if (type !== undefined) { + } else if (type !== undefined && !(type instanceof NeverType)) { flatTypes.push(type); } return flatTypes; diff --git a/src/TypeFormatter/NeverTypeFormatter.ts b/src/TypeFormatter/NeverTypeFormatter.ts new file mode 100644 index 000000000..c21116419 --- /dev/null +++ b/src/TypeFormatter/NeverTypeFormatter.ts @@ -0,0 +1,16 @@ +import { Definition } from "../Schema/Definition"; +import { SubTypeFormatter } from "../SubTypeFormatter"; +import { BaseType } from "../Type/BaseType"; +import { NeverType } from "../Type/NeverType"; + +export class NeverTypeFormatter implements SubTypeFormatter { + public supportsType(type: NeverType): boolean { + return type instanceof NeverType; + } + public getDefinition(type: NeverType): Definition { + return { not: {} }; + } + public getChildren(type: NeverType): BaseType[] { + return []; + } +} diff --git a/test/valid-data-other.test.ts b/test/valid-data-other.test.ts index 32b6e7ab4..833df917d 100644 --- a/test/valid-data-other.test.ts +++ b/test/valid-data-other.test.ts @@ -65,6 +65,9 @@ describe("valid-data-other", () => { it("undefined-union", assertValidSchema("undefined-union", "MyType")); it("undefined-property", assertValidSchema("undefined-property", "MyType")); + it("never", assertValidSchema("never", "BasicNever")); + it("never-record", assertValidSchema("never-record", "Mapped")); + it("any-unknown", assertValidSchema("any-unknown", "MyObject")); it("multiple-roots1", assertValidSchema("multiple-roots1")); diff --git a/test/valid-data/never-record/main.ts b/test/valid-data/never-record/main.ts new file mode 100644 index 000000000..494d97602 --- /dev/null +++ b/test/valid-data/never-record/main.ts @@ -0,0 +1,3 @@ +// This form is recommended by the typescript-eslint rule "ban-types" to mean +// "an object with no properties" +export type Mapped = Record; diff --git a/test/valid-data/never-record/schema.json b/test/valid-data/never-record/schema.json new file mode 100644 index 000000000..b2b7cfed9 --- /dev/null +++ b/test/valid-data/never-record/schema.json @@ -0,0 +1,12 @@ +{ + "$ref": "#/definitions/Mapped", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "Mapped": { + "additionalProperties": { + "not": {} + }, + "type": "object" + } + } +} diff --git a/test/valid-data/never/main.ts b/test/valid-data/never/main.ts new file mode 100644 index 000000000..c0c86fece --- /dev/null +++ b/test/valid-data/never/main.ts @@ -0,0 +1 @@ +export type BasicNever = never; diff --git a/test/valid-data/never/schema.json b/test/valid-data/never/schema.json new file mode 100644 index 000000000..443228581 --- /dev/null +++ b/test/valid-data/never/schema.json @@ -0,0 +1,9 @@ +{ + "$ref": "#/definitions/BasicNever", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "BasicNever": { + "not": {} + } + } +}