forked from vega/ts-json-schema-generator
/
AnnotatedTypeFormatter.ts
89 lines (78 loc) · 2.97 KB
/
AnnotatedTypeFormatter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { Definition } from "../Schema/Definition";
import { SubTypeFormatter } from "../SubTypeFormatter";
import { AnnotatedType } from "../Type/AnnotatedType";
import { BaseType } from "../Type/BaseType";
import { UnionType } from "../Type/UnionType";
import { TypeFormatter } from "../TypeFormatter";
import { derefType } from "../Utils/derefType";
export function makeNullable(def: Definition): Definition {
const union: Definition[] | undefined = (def.oneOf as Definition[]) || def.anyOf;
if (union && union.filter((d: Definition) => d.type === "null").length === 0) {
union.push({ type: "null" });
} else if (def.type && def.type !== "object") {
if (Array.isArray(def.type)) {
if (def.type.indexOf("null") === -1) {
def.type.push("null");
}
} else if (def.type !== "null") {
def.type = [def.type, "null"];
}
// enums need null as an option
if (def.enum && def.enum.indexOf(null) === -1) {
def.enum.push(null);
}
} else {
const subdef: Definition = {};
if ("anyOf" in def) {
for (const d of def.anyOf as Definition[]) {
if (d.type === "null") {
return def;
}
}
}
for (const key of Object.keys(def) as (keyof Definition)[]) {
if (key !== "description" && key !== "title" && key !== "default") {
(subdef as any)[key] = def[key] as any;
delete def[key];
}
}
def.anyOf = [subdef, { type: "null" }];
}
return def;
}
export class AnnotatedTypeFormatter implements SubTypeFormatter {
public constructor(protected childTypeFormatter: TypeFormatter) {}
public supportsType(type: AnnotatedType): boolean {
return type instanceof AnnotatedType;
}
public getDefinition(type: AnnotatedType): Definition {
const annotations = type.getAnnotations();
if ("discriminator" in annotations) {
const derefed = derefType(type.getType());
if (derefed instanceof UnionType) {
derefed.setDiscriminator(annotations.discriminator);
delete annotations.discriminator;
} else {
throw new Error(
`Cannot assign discriminator tag to type: ${JSON.stringify(
derefed
)}. This tag can only be assigned to union types.`
);
}
}
const def: Definition = {
...this.childTypeFormatter.getDefinition(type.getType()),
...type.getAnnotations(),
};
if ("$ref" in def && "type" in def) {
delete def["$ref"];
}
if (type.isNullable()) {
return makeNullable(def);
}
return def;
}
public getChildren(type: AnnotatedType): BaseType[] {
return this.childTypeFormatter.getChildren(type.getType());
}
}