Skip to content

Commit 24c1428

Browse files
authored
Merge pull request #35 from code0-tech/feat/#33
Add DataType | DataType[] to getVariantFromType
2 parents 90a1ffc + 126a9c3 commit 24c1428

4 files changed

Lines changed: 111 additions & 73 deletions

File tree

src/extraction/getTypeVariant.ts

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,53 +10,85 @@ export enum DataTypeVariant {
1010
NODE
1111
}
1212

13+
export interface TypeVariantResult {
14+
identifier: string;
15+
variant: DataTypeVariant;
16+
}
17+
1318
/**
14-
* Determines the variant of a given TypeScript type string using the TS compiler.
19+
* Determines the variant of a given TypeScript types string or DataType(s) using the TS compiler.
20+
* - If types is a string: returns one result with the string as identifier
21+
* - If types is a DataType: returns one result with DataType.identifier
22+
* - If types is a DataType[]: returns results for each DataType with their identifiers
1523
*/
1624
export const getTypeVariant = (
17-
type?: string,
25+
types?: string | DataType | DataType[],
1826
dataTypes?: DataType[]
19-
): DataTypeVariant => {
27+
): TypeVariantResult[] => {
2028
const typeDefs = getSharedTypeDeclarations(dataTypes);
2129

22-
// We declare a variable with the type to probe it
23-
const sourceCode = `
24-
${typeDefs}
25-
type TargetType = ${type};
26-
const val: TargetType = {} as any;
27-
`;
28-
29-
const fileName = `index.ts`;
30-
const host = createCompilerHost(fileName, sourceCode);
31-
const sourceFile = host.getSourceFile(fileName)!;
32-
const program = host.languageService.getProgram()!;
33-
const checker = program.getTypeChecker();
34-
35-
let discoveredVariant: DataTypeVariant = DataTypeVariant.TYPE;
36-
37-
const visit = (node: ts.Node) => {
38-
if (ts.isVariableDeclaration(node) && node.name.getText() === "val") {
39-
const type = checker.getTypeAtLocation(node);
40-
41-
if (type.getCallSignatures().length > 0) {
42-
discoveredVariant = DataTypeVariant.NODE;
43-
} else if (checker.isArrayType(type)) {
44-
discoveredVariant = DataTypeVariant.ARRAY;
45-
} else if (
46-
type.isStringLiteral() ||
47-
type.isNumberLiteral() ||
48-
(type.getFlags() & (ts.TypeFlags.String | ts.TypeFlags.Number | ts.TypeFlags.Boolean | ts.TypeFlags.EnumLiteral | ts.TypeFlags.BigInt | ts.TypeFlags.ESSymbol)) !== 0
49-
) {
50-
discoveredVariant = DataTypeVariant.PRIMITIVE;
51-
} else if (type.isClassOrInterface() || (type.getFlags() & ts.TypeFlags.Object) !== 0) {
52-
discoveredVariant = DataTypeVariant.OBJECT;
53-
} else {
54-
discoveredVariant = DataTypeVariant.TYPE;
30+
// Determine what we're analyzing
31+
const isStringType = typeof types === "string";
32+
const isArrayType = Array.isArray(types);
33+
const typesToAnalyze = isArrayType ? (types as DataType[]) : isStringType ? [{ identifier: types, type: types }] : [types];
34+
const identifiers = isArrayType
35+
? (types as DataType[]).map(t => t.identifier)
36+
: isStringType
37+
? [types as string]
38+
: [(types as DataType).identifier];
39+
40+
const results: TypeVariantResult[] = [];
41+
42+
for (let i = 0; i < typesToAnalyze.length; i++) {
43+
const currentType = typesToAnalyze[i];
44+
const currentIdentifier = identifiers[i];
45+
const typeString = isStringType ? (types as string) : (currentType as DataType).type;
46+
47+
// We declare a variable with the types to probe it
48+
const sourceCode = `
49+
${typeDefs}
50+
${typeString ? `type TargetType = ${typeString};` : ""}
51+
const val: TargetType = {} as any;
52+
`;
53+
54+
const fileName = `index.ts`;
55+
const host = createCompilerHost(fileName, sourceCode);
56+
const sourceFile = host.getSourceFile(fileName)!;
57+
const program = host.languageService.getProgram()!;
58+
const checker = program.getTypeChecker();
59+
60+
let discoveredVariant: DataTypeVariant = DataTypeVariant.TYPE;
61+
62+
const visit = (node: ts.Node) => {
63+
if (ts.isVariableDeclaration(node) && node.name.getText() === "val") {
64+
const type = checker.getTypeAtLocation(node);
65+
66+
if (type.getCallSignatures().length > 0) {
67+
discoveredVariant = DataTypeVariant.NODE;
68+
} else if (checker.isArrayType(type)) {
69+
discoveredVariant = DataTypeVariant.ARRAY;
70+
} else if (
71+
type.isStringLiteral() ||
72+
type.isNumberLiteral() ||
73+
(type.getFlags() & (ts.TypeFlags.String | ts.TypeFlags.Number | ts.TypeFlags.Boolean | ts.TypeFlags.EnumLiteral | ts.TypeFlags.BigInt | ts.TypeFlags.ESSymbol)) !== 0
74+
) {
75+
discoveredVariant = DataTypeVariant.PRIMITIVE;
76+
} else if (type.isClassOrInterface() || (type.getFlags() & ts.TypeFlags.Object) !== 0) {
77+
discoveredVariant = DataTypeVariant.OBJECT;
78+
} else {
79+
discoveredVariant = DataTypeVariant.TYPE;
80+
}
5581
}
56-
}
57-
ts.forEachChild(node, visit);
58-
};
82+
ts.forEachChild(node, visit);
83+
};
84+
85+
visit(sourceFile);
86+
87+
results.push({
88+
identifier: currentIdentifier!,
89+
variant: discoveredVariant
90+
});
91+
}
5992

60-
visit(sourceFile);
61-
return discoveredVariant;
93+
return results;
6294
};

src/suggestion/getNodeSuggestions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const getNodeSuggestions = (
1414

1515
let functionToSuggest = functions
1616

17-
const typeVariant = type ? getTypeVariant(type, dataTypes) : null;
17+
const typeVariant = type ? getTypeVariant(type, dataTypes)[0].variant : null;
1818

1919
if (type && functions && typeVariant !== DataTypeVariant.NODE) {
2020
function getGenericsCount(input: string): number {

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export function generateFlowSourceCode(
178178

179179
if (!isForInference) {
180180
const expectedType = nodeTypes.parameters[index];
181-
const isFunctionType = expectedType ? getTypeVariant(expectedType, dataTypes) === DataTypeVariant.NODE : false;
181+
const isFunctionType = expectedType ? getTypeVariant(expectedType, dataTypes)[0].variant === DataTypeVariant.NODE : false;
182182

183183
if (isFunctionType) {
184184
const lambdaArgName = `p_${sanitizeId(nodeId)}_${index}`;

test/getTypeVariant.test.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,64 @@ import {DATA_TYPES, FUNCTION_SIGNATURES} from "./data";
44
import {getTypesFromNode} from "../src";
55

66
describe('getTypeVariant', () => {
7-
it('sollte PRIMITIVE für einfache Typen zurückgeben', () => {
8-
expect(getTypeVariant("string", DATA_TYPES)).toBe(DataTypeVariant.PRIMITIVE);
9-
expect(getTypeVariant("number", DATA_TYPES)).toBe(DataTypeVariant.PRIMITIVE);
10-
expect(getTypeVariant("boolean", DATA_TYPES)).toBe(DataTypeVariant.PRIMITIVE);
7+
it('identifies native TypeScript primitives (string, number, boolean) as PRIMITIVE variant', () => {
8+
expect(getTypeVariant("string", DATA_TYPES)[0].variant).toBe(DataTypeVariant.PRIMITIVE);
9+
expect(getTypeVariant("number", DATA_TYPES)[0].variant).toBe(DataTypeVariant.PRIMITIVE);
10+
expect(getTypeVariant("boolean", DATA_TYPES)[0].variant).toBe(DataTypeVariant.PRIMITIVE);
1111
});
1212

13-
it('sollte ARRAY für Array-Typen zurückgeben', () => {
14-
expect(getTypeVariant("string[]", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
15-
expect(getTypeVariant("Array<number>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
13+
it('recognizes both bracket notation and generic syntax for array types as ARRAY variant', () => {
14+
expect(getTypeVariant("string[]", DATA_TYPES)[0].variant).toBe(DataTypeVariant.ARRAY);
15+
expect(getTypeVariant("Array<number>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.ARRAY);
1616
});
1717

18-
it('sollte OBJECT für Interfaces oder Objekte mit Properties zurückgeben', () => {
19-
expect(getTypeVariant("{ name: string }", DATA_TYPES)).toBe(DataTypeVariant.OBJECT);
20-
expect(getTypeVariant("{}", DATA_TYPES)).toBe(DataTypeVariant.OBJECT);
21-
expect(getTypeVariant("OBJECT<any>", DATA_TYPES)).toBe(DataTypeVariant.OBJECT);
18+
it('classifies object literals and interface-like structures as OBJECT variant', () => {
19+
expect(getTypeVariant("{ name: string }", DATA_TYPES)[0].variant).toBe(DataTypeVariant.OBJECT);
20+
expect(getTypeVariant("{}", DATA_TYPES)[0].variant).toBe(DataTypeVariant.OBJECT);
21+
expect(getTypeVariant("OBJECT<any>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.OBJECT);
2222
});
2323

24-
it('sollte TYPE für einfache Type-Aliase oder void zurückgeben', () => {
25-
expect(getTypeVariant("void", DATA_TYPES)).toBe(DataTypeVariant.TYPE);
26-
expect(getTypeVariant("any", DATA_TYPES)).toBe(DataTypeVariant.TYPE);
24+
it('marks special types like void and any as TYPE variant', () => {
25+
expect(getTypeVariant("void", DATA_TYPES)[0].variant).toBe(DataTypeVariant.TYPE);
26+
expect(getTypeVariant("any", DATA_TYPES)[0].variant).toBe(DataTypeVariant.TYPE);
2727
});
2828

29-
it('sollte LIST (NUMBER) als ARRAY erkennen (wenn in DATA_TYPES definiert)', () => {
30-
// In data.ts ist LIST als T[] definiert
31-
expect(getTypeVariant("LIST<NUMBER>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
32-
expect(getTypeVariant("LIST<unknown>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
33-
expect(getTypeVariant("LIST<T>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
29+
it('resolves generic LIST type aliases to ARRAY variant regardless of type parameter', () => {
30+
// LIST<T> is defined as T[] in data.ts, so all LIST variants should be arrays
31+
expect(getTypeVariant("LIST<NUMBER>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.ARRAY);
32+
expect(getTypeVariant("LIST<unknown>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.ARRAY);
33+
expect(getTypeVariant("LIST<T>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.ARRAY);
3434
});
3535

36-
it('sollte NODE für Funktionstypen wie CONSUMER zurückgeben', () => {
37-
// In data.ts ist CONSUMER als (item:R) => void definiert
38-
expect(getTypeVariant("CONSUMER<NUMBER>", DATA_TYPES)).toBe(DataTypeVariant.NODE);
36+
it('identifies CONSUMER function types with generic parameters as NODE variant', () => {
37+
// CONSUMER<T> represents a callback function (T) => void
38+
expect(getTypeVariant("CONSUMER<NUMBER>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.NODE);
3939
});
4040

41-
it('sollte NODE für Funktionstypen wie RUNNABLE zurückgeben', () => {
42-
// In data.ts ist CONSUMER als (item:R) => void definiert
43-
expect(getTypeVariant("RUNNABLE", DATA_TYPES)).toBe(DataTypeVariant.NODE);
41+
it('identifies parameterless function types like RUNNABLE as NODE variant', () => {
42+
// RUNNABLE represents () => void with no parameters
43+
expect(getTypeVariant("RUNNABLE", DATA_TYPES)[0].variant).toBe(DataTypeVariant.NODE);
4444
});
4545

46-
it('sollte NODE für Funktionstypen wie PREDICATE zurückgeben', () => {
47-
// In data.ts ist CONSUMER als (item:R) => void definiert
48-
expect(getTypeVariant("PREDICATE<NUMBER>", DATA_TYPES)).toBe(DataTypeVariant.NODE);
46+
it('identifies PREDICATE function types with generic parameters as NODE variant', () => {
47+
// PREDICATE<T> represents a boolean-returning function (T) => boolean
48+
expect(getTypeVariant("PREDICATE<NUMBER>", DATA_TYPES)[0].variant).toBe(DataTypeVariant.NODE);
4949
});
5050

51+
it('correctly identifies type variants when retrieved directly from DATA_TYPES registry', () => {
52+
// Verify that types stored in DATA_TYPES are properly classified
53+
expect(getTypeVariant(DATA_TYPES.find(dt => dt.identifier === "LIST"), DATA_TYPES)[0].variant).toBe(DataTypeVariant.ARRAY);
54+
expect(getTypeVariant(DATA_TYPES.find(dt => dt.identifier === "HTTP_METHOD"), DATA_TYPES)[0].variant).toBe(DataTypeVariant.TYPE);
55+
});
5156

52-
it("Check if", () => {
57+
it('recognizes callback parameter types in real function signatures as NODE variant', () => {
58+
// When extracting types from std::list::for_each, the second parameter (consumer) should be NODE
5359
const types = getTypesFromNode({
5460
functionDefinition: {
5561
identifier: "std::list::for_each"
5662
}
5763
}, FUNCTION_SIGNATURES, DATA_TYPES)
58-
expect(getTypeVariant(types.parameters[1], DATA_TYPES)).toBe(DataTypeVariant.NODE);
64+
expect(getTypeVariant(types.parameters[1], DATA_TYPES)[0].variant).toBe(DataTypeVariant.NODE);
5965
});
6066
});
6167

0 commit comments

Comments
 (0)