-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgetTypeVariant.ts
More file actions
94 lines (80 loc) · 3.47 KB
/
Copy pathgetTypeVariant.ts
File metadata and controls
94 lines (80 loc) · 3.47 KB
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
90
91
92
93
94
import ts from "typescript";
import {createCompilerHost, getSharedTypeDeclarations} from "../utils";
import {DataType} from "@code0-tech/sagittarius-graphql-types";
export enum DataTypeVariant {
PRIMITIVE,
TYPE,
ARRAY,
OBJECT,
NODE
}
export interface TypeVariantResult {
identifier: string;
variant: DataTypeVariant;
}
/**
* Determines the variant of a given TypeScript types string or DataType(s) using the TS compiler.
* - If types is a string: returns one result with the string as identifier
* - If types is a DataType: returns one result with DataType.identifier
* - If types is a DataType[]: returns results for each DataType with their identifiers
*/
export const getTypeVariant = (
types?: string | DataType | DataType[],
dataTypes?: DataType[]
): TypeVariantResult[] => {
const typeDefs = getSharedTypeDeclarations(dataTypes);
// Determine what we're analyzing
const isStringType = typeof types === "string";
const isArrayType = Array.isArray(types);
const typesToAnalyze = isArrayType ? (types as DataType[]) : isStringType ? [{ identifier: types, type: types }] : [types];
const identifiers = isArrayType
? (types as DataType[]).map(t => t?.identifier)
: isStringType
? [types as string]
: [(types as DataType)?.identifier];
const results: TypeVariantResult[] = [];
for (let i = 0; i < typesToAnalyze.length; i++) {
const currentType = typesToAnalyze[i];
const currentIdentifier = identifiers[i];
const typeString = isStringType ? (types as string) : (currentType as DataType).type;
// We declare a variable with the types to probe it
const sourceCode = `
${typeDefs}
${typeString ? `type TargetType = ${typeString};` : ""}
const val: TargetType = {} as any;
`;
const fileName = `index.ts`;
const host = createCompilerHost(fileName, sourceCode);
const sourceFile = host.getSourceFile(fileName)!;
const program = host.languageService.getProgram()!;
const checker = program.getTypeChecker();
let discoveredVariant: DataTypeVariant = DataTypeVariant.TYPE;
const visit = (node: ts.Node) => {
if (ts.isVariableDeclaration(node) && node.name.getText() === "val") {
const type = checker.getTypeAtLocation(node);
if (type.getCallSignatures().length > 0) {
discoveredVariant = DataTypeVariant.NODE;
} else if (checker.isArrayType(type)) {
discoveredVariant = DataTypeVariant.ARRAY;
} else if (
type.isStringLiteral() ||
type.isNumberLiteral() ||
(type.getFlags() & (ts.TypeFlags.String | ts.TypeFlags.Number | ts.TypeFlags.Boolean | ts.TypeFlags.EnumLiteral | ts.TypeFlags.BigInt | ts.TypeFlags.ESSymbol)) !== 0
) {
discoveredVariant = DataTypeVariant.PRIMITIVE;
} else if (type.isClassOrInterface() || (type.getFlags() & ts.TypeFlags.Object) !== 0) {
discoveredVariant = DataTypeVariant.OBJECT;
} else {
discoveredVariant = DataTypeVariant.TYPE;
}
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
results.push({
identifier: currentIdentifier!,
variant: discoveredVariant
});
}
return results;
};