UNPKG

ts-json-schema-generator

Version:

Generate JSON schema from your Typescript sources

140 lines (120 loc) 5.07 kB
import { translate } from "../NodeParser/IntersectionNodeParser.js"; import { AnyType } from "../Type/AnyType.js"; import { ArrayType } from "../Type/ArrayType.js"; import { BaseType } from "../Type/BaseType.js"; import { IntersectionType } from "../Type/IntersectionType.js"; import { LiteralType } from "../Type/LiteralType.js"; import { NumberType } from "../Type/NumberType.js"; import { ObjectType } from "../Type/ObjectType.js"; import type { StringType } from "../Type/StringType.js"; import { TupleType } from "../Type/TupleType.js"; import { UndefinedType } from "../Type/UndefinedType.js"; import { UnionType } from "../Type/UnionType.js"; import { derefAnnotatedType, derefType } from "./derefType.js"; import { preserveAnnotation } from "./preserveAnnotation.js"; import { uniqueArray } from "./uniqueArray.js"; import { uniqueTypeArray } from "./uniqueTypeArray.js"; function uniqueLiterals(types: LiteralType[]): LiteralType[] { const values = types.map((type) => type.getValue()); return uniqueArray(values).map((value) => new LiteralType(value)); } export function getTypeKeys(type: BaseType): LiteralType[] { type = derefType(type); if (type instanceof IntersectionType || type instanceof UnionType) { return uniqueLiterals( type.getTypes().reduce((result: LiteralType[], subType) => [...result, ...getTypeKeys(subType)], []), ); } if (type instanceof TupleType) { return type.getTypes().map((_it, idx) => new LiteralType(idx)); } if (type instanceof ObjectType) { const objectProperties = type.getProperties().map((it) => new LiteralType(it.getName())); return uniqueLiterals( type .getBaseTypes() .reduce( (result: LiteralType[], parentType) => [...result, ...getTypeKeys(parentType)], objectProperties, ), ); } return []; } export function getTypeByKey(type: BaseType, index: LiteralType | StringType | NumberType): BaseType | undefined { type = derefType(type); if (type instanceof IntersectionType || type instanceof UnionType) { let subTypes: BaseType[] = []; // we use the annotation from the first type so we need to get a reference to it let firstType: BaseType | undefined; for (const subType of type.getTypes()) { const subKeyType = getTypeByKey(subType, index); if (subKeyType) { subTypes.push(subKeyType); if (!firstType) { firstType = subKeyType; } } } subTypes = uniqueTypeArray(subTypes); let returnType: BaseType | undefined = undefined; if (subTypes.length == 1) { return firstType; } else if (subTypes.length > 1) { if (type instanceof UnionType) { returnType = new UnionType(subTypes); } else { returnType = translate(subTypes); } } if (!returnType) { return undefined; } if (!firstType) { return returnType; } return preserveAnnotation(firstType, returnType); } if (type instanceof TupleType && index instanceof LiteralType) { return type.getTypes().find((it, idx) => idx === index.getValue()); } if (type instanceof ArrayType && index instanceof NumberType) { return type.getItem(); } if (type instanceof ObjectType) { if (index instanceof LiteralType) { const property = type.getProperties().find((it) => it.getName() === index.getValue()); if (property) { const propertyType = property.getType(); if (propertyType === undefined) { return undefined; } let newPropType = derefAnnotatedType(propertyType); if (!property.isRequired()) { if (newPropType instanceof UnionType) { if (!newPropType.getTypes().some((subType) => subType instanceof UndefinedType)) { newPropType = new UnionType([...newPropType.getTypes(), new UndefinedType()]); } } else { newPropType = new UnionType([newPropType, new UndefinedType()]); } } return preserveAnnotation(propertyType, newPropType); } } const additionalProperty = type.getAdditionalProperties(); if (additionalProperty instanceof BaseType) { return additionalProperty; } else if (additionalProperty === true) { return new AnyType(); } for (const subType of type.getBaseTypes()) { const subKeyType = getTypeByKey(subType, index); if (subKeyType) { return subKeyType; } } return undefined; } return undefined; }