ts-json-schema-generator
Version:
Generate JSON schema from your Typescript sources
75 lines (68 loc) • 2.54 kB
text/typescript
import { JSONSchema7 } from "json-schema";
import { Definition } from "../Schema/Definition";
import { SubTypeFormatter } from "../SubTypeFormatter";
import { BaseType } from "../Type/BaseType";
import { UnionType } from "../Type/UnionType";
import { TypeFormatter } from "../TypeFormatter";
import { uniqueArray } from "../Utils/uniqueArray";
export class UnionTypeFormatter implements SubTypeFormatter {
public constructor(private childTypeFormatter: TypeFormatter) {}
public supportsType(type: UnionType): boolean {
return type instanceof UnionType;
}
public getDefinition(type: UnionType): Definition {
const definitions = type.getTypes().map((item) => this.childTypeFormatter.getDefinition(item));
// TODO: why is this not covered by LiteralUnionTypeFormatter?
// special case for string literals | string -> string
let stringType = true;
let oneNotEnum = false;
for (const def of definitions) {
if (def.type !== "string") {
stringType = false;
break;
}
if (def.enum === undefined) {
oneNotEnum = true;
}
}
if (stringType && oneNotEnum) {
const values = [];
for (const def of definitions) {
if (def.enum) {
values.push(...def.enum);
} else if (def.const) {
values.push(def.const);
} else {
return {
type: "string",
};
}
}
return {
type: "string",
enum: values,
};
}
const flattenedDefinitions: JSONSchema7[] = [];
// Flatten anyOf inside anyOf unless the anyOf has an annotation
for (const def of definitions) {
if (Object.keys(def) === ["anyOf"]) {
flattenedDefinitions.push(...(def.anyOf as any));
} else {
flattenedDefinitions.push(def);
}
}
return flattenedDefinitions.length > 1
? {
anyOf: flattenedDefinitions,
}
: flattenedDefinitions[0];
}
public getChildren(type: UnionType): BaseType[] {
return uniqueArray(
type
.getTypes()
.reduce((result: BaseType[], item) => [...result, ...this.childTypeFormatter.getChildren(item)], [])
);
}
}