UNPKG

ts-json-schema-generator

Version:

Generate JSON schema from your Typescript sources

83 lines (71 loc) 3.1 kB
import ts from "typescript"; import { Context, NodeParser } from "../NodeParser"; import { SubNodeParser } from "../SubNodeParser"; import { BaseType } from "../Type/BaseType"; import { IntersectionType } from "../Type/IntersectionType"; import { PrimitiveType } from "../Type/PrimitiveType"; import { UnionType } from "../Type/UnionType"; import { derefType } from "../Utils/derefType"; import { uniqueTypeArray } from "../Utils/uniqueTypeArray"; import { UndefinedType } from "../Type/UndefinedType"; export class IntersectionNodeParser implements SubNodeParser { public constructor(private typeChecker: ts.TypeChecker, private childNodeParser: NodeParser) {} public supportsNode(node: ts.IntersectionTypeNode): boolean { return node.kind === ts.SyntaxKind.IntersectionType; } public createType(node: ts.IntersectionTypeNode, context: Context): BaseType | undefined { const types = node.types.map((subnode) => this.childNodeParser.createType(subnode, context)); // if any type is undefined (never), an intersection type should be undefined (never) if (types.filter((t) => t === undefined).length) { return undefined; } return translate(types as BaseType[]); } } /** * Translates the given intersection type into a union type if necessary so `A & (B | C)` becomes * `(A & B) | (A & C)`. If no translation is needed then the original intersection type is returned. */ export function translate(types: BaseType[]): BaseType | undefined { types = uniqueTypeArray(types as BaseType[]); if (types.length == 1) { return types[0]; } const unions = types.map((type) => { const derefed = derefType(type); return derefed instanceof UnionType ? derefed.getTypes() : [type]; }); const result: BaseType[] = []; function process(i: number, t: BaseType[] = []) { for (const type of unions[i]) { let currentTypes = [...t, type]; if (i < unions.length - 1) { process(i + 1, currentTypes); } else { currentTypes = uniqueTypeArray(currentTypes); if (currentTypes.some((c) => c instanceof UndefinedType)) { // never result.push(new UndefinedType()); } else { const primitives = currentTypes.filter((c) => c instanceof PrimitiveType); if (primitives.length === 1) { result.push(primitives[0]); } else if (primitives.length > 1) { // conflict -> ignore } else if (currentTypes.length === 1) { result.push(currentTypes[0]); } else { result.push(new IntersectionType(currentTypes)); } } } } } process(0); if (result.length === 1) { return result[0]; } else if (result.length > 1) { return new UnionType(result); } return undefined; }