diff --git a/demo/cases.json b/demo/cases.json index 56d7269..bbb2556 100644 --- a/demo/cases.json +++ b/demo/cases.json @@ -421,6 +421,9 @@ }, { "type": "null" + }, + { + "const": false } ] }, @@ -550,7 +553,8 @@ "enum": [ "foo", "bar", - null + null, + false ] }, "InterfaceExtends": { diff --git a/demo/cases.ts b/demo/cases.ts index c29f9a5..9f97b1f 100644 --- a/demo/cases.ts +++ b/demo/cases.ts @@ -43,14 +43,14 @@ type TypeUnion4 = typeUnionMember2: string; } type TypeUnion5 = TypeLiteral | Interface -type TypeUnion8 = 'foo' | 'bar' | null +type TypeUnion8 = 'foo' | 'bar' | null | false type TypeUnion = { typeUnionMember1: TypeUnion1; typeUnionMember2: TypeUnion2; typeUnionMember3: TypeUnion3; typeUnionMember4: TypeUnion4; typeUnionMember5: TypeUnion5; - typeUnionMember6: string | null; + typeUnionMember6: string | null | false; typeUnionMember7: 'foo' | 'bar'; typeUnionMember8: TypeUnion8; } diff --git a/demo/debug.json b/demo/debug.json index 3c7ed80..879046a 100644 --- a/demo/debug.json +++ b/demo/debug.json @@ -244,12 +244,36 @@ ] }, { - "kind": "string", + "kind": "union", "name": "TypeUnion8", - "enums": [ - "foo", - "bar", - null + "members": [ + { + "kind": "enum", + "type": "string", + "name": "string", + "enums": [ + "foo" + ] + }, + { + "kind": "enum", + "type": "string", + "name": "string", + "enums": [ + "bar" + ] + }, + { + "kind": "null" + }, + { + "kind": "enum", + "type": "boolean", + "name": "boolean", + "enums": [ + false + ] + } ] }, { @@ -301,6 +325,14 @@ }, { "kind": "null" + }, + { + "kind": "enum", + "type": "boolean", + "name": "boolean", + "enums": [ + false + ] } ] } diff --git a/online/variables.ts b/online/variables.ts index f07c3bb..c1477b1 100644 --- a/online/variables.ts +++ b/online/variables.ts @@ -50,14 +50,14 @@ type TypeUnion4 = typeUnionMember2: string; } type TypeUnion5 = TypeLiteral | Interface -type TypeUnion8 = 'foo' | 'bar' | null +type TypeUnion8 = 'foo' | 'bar' | null | false type TypeUnion = { typeUnionMember1: TypeUnion1; typeUnionMember2: TypeUnion2; typeUnionMember3: TypeUnion3; typeUnionMember4: TypeUnion4; typeUnionMember5: TypeUnion5; - typeUnionMember6: string | null; + typeUnionMember6: string | null | false; typeUnionMember7: 'foo' | 'bar'; typeUnionMember8: TypeUnion8; } diff --git a/src/json-schema-generator.ts b/src/json-schema-generator.ts index fa85e55..1f30a7f 100644 --- a/src/json-schema-generator.ts +++ b/src/json-schema-generator.ts @@ -83,10 +83,7 @@ function getJsonSchemaProperty(memberType: Type | ObjectModel | ArrayModel | Uni } else if (memberType.kind === 'string') { return getJsonSchemaPropertyOfString(memberType) } else if (memberType.kind === 'union') { - return { - type: undefined, - anyOf: memberType.members.map(m => getJsonSchemaProperty(m)) - } + return getJsonSchemaPropertyOfUnion(memberType as UnionModel) } else if (memberType.kind === 'null') { return { type: 'null' @@ -98,6 +95,27 @@ function getJsonSchemaProperty(memberType: Type | ObjectModel | ArrayModel | Uni } } +function getJsonSchemaPropertyOfUnion(memberType: UnionModel) { + if (memberType.members.every(m => m.kind === 'enum' || m.kind === 'null')) { + let enums: any[] = [] + for (const member of memberType.members) { + if (member.kind === 'enum') { + enums = enums.concat(member.enums) + } else if (member.kind === 'null') { + enums.push(null) + } + } + return { + type: undefined, + enum: enums + } + } + return { + type: undefined, + anyOf: memberType.members.map(m => getJsonSchemaProperty(m)) + } +} + function getJsonSchemaPropertyOfString(memberType: StringType) { if (memberType.enums) { if (memberType.enums.length === 1) { diff --git a/src/parser.ts b/src/parser.ts index 49c58ab..0e01134 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -9,7 +9,9 @@ import { NumberType, StringType, BooleanType, - ReferenceType + ReferenceType, + NumberModel, + StringModel } from './utils' export class Parser { @@ -225,17 +227,16 @@ export class Parser { } private handleUnionTypeOfLiteralType(unionType: ts.UnionTypeNode, declarationName: ts.Identifier) { - let enumType: 'string' | 'number' | undefined + let enumType: 'string' | 'number' | 'boolean' | undefined const enums: any[] = [] for (const childType of unionType.types) { if (childType.kind === ts.SyntaxKind.LiteralType) { - const literalType = childType as ts.LiteralTypeNode - if (literalType.literal.kind === ts.SyntaxKind.StringLiteral) { - enumType = 'string' - enums.push(literalType.literal.text) - } else if (literalType.literal.kind === ts.SyntaxKind.NumericLiteral) { - enumType = 'number' - enums.push(+literalType.literal.text) + const { type, value } = this.getEnumOfLiteralType(childType as ts.LiteralTypeNode) + if (type !== undefined) { + enumType = type + } + if (value !== undefined) { + enums.push(value) } } else if (childType.kind === ts.SyntaxKind.NullKeyword) { enums.push(null) @@ -243,20 +244,28 @@ export class Parser { } if (enumType) { if (enumType === 'string') { - const model: Model = { + const model: StringModel = { kind: enumType, name: declarationName.text, enums } this.models.push(model) - } else { - const model: Model = { + } else if (enumType === 'number') { + const model: NumberModel = { kind: enumType, type: enumType, name: declarationName.text, enums } this.models.push(model) + } else if (enumType === 'boolean') { + const model: Model = { + kind: 'union', + name: declarationName.text, + members: unionType.types.map(e => this.getType(e)), + entry: undefined + } + this.models.push(model) } } } @@ -362,15 +371,45 @@ export class Parser { } } + private getEnumOfLiteralType(literalType: ts.LiteralTypeNode): { type: 'string' | 'number' | 'boolean' | undefined, value: any } { + if (literalType.literal.kind === ts.SyntaxKind.StringLiteral) { + return { + type: 'string', + value: literalType.literal.text + } + } + if (literalType.literal.kind === ts.SyntaxKind.NumericLiteral) { + return { + type: 'number', + value: +literalType.literal.text + } + } + if (literalType.literal.kind === ts.SyntaxKind.TrueKeyword) { + return { + type: 'boolean', + value: true + } + } else if (literalType.literal.kind === ts.SyntaxKind.FalseKeyword) { + return { + type: 'boolean', + value: false + } + } + return { + type: undefined, + value: undefined + } + } + private getTypeOfLiteralType(literalType: ts.LiteralTypeNode): Type { - let enumType: 'string' | 'number' | undefined + let enumType: 'string' | 'number' | 'boolean' | undefined const enums: any[] = [] - if (literalType.literal.kind === ts.SyntaxKind.StringLiteral) { - enumType = 'string' - enums.push(literalType.literal.text) - } else if (literalType.literal.kind === ts.SyntaxKind.NumericLiteral) { - enumType = 'number' - enums.push(+literalType.literal.text) + const { type, value } = this.getEnumOfLiteralType(literalType) + if (type !== undefined) { + enumType = type + } + if (value !== undefined) { + enums.push(value) } if (enumType) { return { @@ -387,16 +426,15 @@ export class Parser { private getTypeOfUnionType(unionType: ts.UnionTypeNode): Type { if (unionType.types.every(u => u.kind === ts.SyntaxKind.LiteralType)) { - let enumType: 'string' | 'number' | undefined + let enumType: 'string' | 'number' | 'boolean' | undefined const enums: any[] = [] for (const childType of unionType.types) { - const literalType = childType as ts.LiteralTypeNode - if (literalType.literal.kind === ts.SyntaxKind.StringLiteral) { - enumType = 'string' - enums.push(literalType.literal.text) - } else if (literalType.literal.kind === ts.SyntaxKind.NumericLiteral) { - enumType = 'number' - enums.push(+literalType.literal.text) + const { type, value } = this.getEnumOfLiteralType(childType as ts.LiteralTypeNode) + if (type !== undefined) { + enumType = type + } + if (value !== undefined) { + enums.push(value) } } if (enumType) { diff --git a/src/utils.ts b/src/utils.ts index b0972d7..a7dfe88 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -38,16 +38,10 @@ export type UnionModel = UnionType & { entry: string | undefined; } -/** - * @public - */ export type StringModel = StringType & { name: string; } -/** - * @public - */ export type NumberModel = NumberType & { name: string; }