/
TypeofNodeParser.ts
74 lines (66 loc) · 3.3 KB
/
TypeofNodeParser.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
import ts from "typescript";
import { LogicError } from "../Error/LogicError";
import { Context, NodeParser } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { ObjectType, ObjectProperty } from "../Type/ObjectType";
import { ReferenceType } from "../Type/ReferenceType";
import { getKey } from "../Utils/nodeKey";
import { LiteralType } from "../Type/LiteralType";
export class TypeofNodeParser implements SubNodeParser {
public constructor(protected typeChecker: ts.TypeChecker, protected childNodeParser: NodeParser) {}
public supportsNode(node: ts.TypeQueryNode): boolean {
return node.kind === ts.SyntaxKind.TypeQuery;
}
public createType(node: ts.TypeQueryNode, context: Context, reference?: ReferenceType): BaseType | undefined {
let symbol = this.typeChecker.getSymbolAtLocation(node.exprName)!;
if (symbol.flags & ts.SymbolFlags.Alias) {
symbol = this.typeChecker.getAliasedSymbol(symbol);
}
const valueDec = symbol.valueDeclaration!;
if (ts.isEnumDeclaration(valueDec)) {
return this.createObjectFromEnum(valueDec, context, reference);
} else if (
ts.isVariableDeclaration(valueDec) ||
ts.isPropertySignature(valueDec) ||
ts.isPropertyDeclaration(valueDec)
) {
if (valueDec.type) {
return this.childNodeParser.createType(valueDec.type, context);
} else if (valueDec.initializer) {
return this.childNodeParser.createType(valueDec.initializer, context);
}
} else if (ts.isClassDeclaration(valueDec)) {
return this.childNodeParser.createType(valueDec, context);
} else if (ts.isPropertyAssignment(valueDec)) {
return this.childNodeParser.createType(valueDec.initializer, context);
} else if (valueDec.kind === ts.SyntaxKind.FunctionDeclaration) {
if ((<ts.FunctionDeclaration>valueDec).type! !== undefined) {
return this.childNodeParser.createType(<ts.Node>(<ts.FunctionDeclaration>valueDec).type, context);
}
}
throw new LogicError(`Invalid type query "${valueDec.getFullText()}" (ts.SyntaxKind = ${valueDec.kind})`);
}
protected createObjectFromEnum(node: ts.EnumDeclaration, context: Context, reference?: ReferenceType): ObjectType {
const id = `typeof-enum-${getKey(node, context)}`;
if (reference) {
reference.setId(id);
reference.setName(id);
}
let type: BaseType | null | undefined = null;
const properties = node.members.map((member) => {
const name = member.name.getText();
if (member.initializer) {
type = this.childNodeParser.createType(member.initializer, context);
} else if (type === null) {
type = new LiteralType(0);
} else if (type instanceof LiteralType && typeof type.getValue() === "number") {
type = new LiteralType(+type.getValue() + 1);
} else {
throw new LogicError(`Enum initializer missing for "${name}"`);
}
return new ObjectProperty(name, type, true);
});
return new ObjectType(id, [], properties, false);
}
}