Skip to content

Commit 5ca7176

Browse files
author
nicosammito
committed
feat: implement getTypeVariant function and corresponding tests for type detection
1 parent 3ab48e3 commit 5ca7176

3 files changed

Lines changed: 95 additions & 0 deletions

File tree

src/getTypeVariant.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import ts from "typescript";
2+
import {ExtendedDataType, getSharedTypeDeclarations, createCompilerHost, DEFAULT_COMPILER_OPTIONS} from "./utils";
3+
4+
export enum Variant {
5+
PRIMITIVE,
6+
TYPE,
7+
ARRAY,
8+
OBJECT,
9+
}
10+
11+
/**
12+
* Determines the variant of a given TypeScript type string using the TS compiler.
13+
*/
14+
export const getTypeVariant = (
15+
typeString: string,
16+
dataTypes: ExtendedDataType[]
17+
): Variant => {
18+
const typeDefs = getSharedTypeDeclarations(dataTypes);
19+
const fileName = `type_probe_${Math.random().toString(36).substring(7)}.ts`;
20+
21+
// We declare a variable with the type to probe it
22+
const sourceCode = `
23+
${typeDefs}
24+
type TargetType = ${typeString};
25+
const val: TargetType = {} as any;
26+
`;
27+
28+
const sourceFile = ts.createSourceFile(fileName, sourceCode, ts.ScriptTarget.Latest);
29+
const host = createCompilerHost(fileName, sourceCode, sourceFile);
30+
const program = ts.createProgram([fileName], DEFAULT_COMPILER_OPTIONS, host);
31+
const checker = program.getTypeChecker();
32+
33+
let discoveredVariant: Variant = Variant.TYPE;
34+
35+
const visit = (node: ts.Node) => {
36+
if (ts.isVariableDeclaration(node) && node.name.getText() === "val") {
37+
const type = checker.getTypeAtLocation(node);
38+
39+
if (checker.isArrayType(type)) {
40+
discoveredVariant = Variant.ARRAY;
41+
} else if (
42+
type.isStringLiteral() ||
43+
type.isNumberLiteral() ||
44+
(type.getFlags() & (ts.TypeFlags.String | ts.TypeFlags.Number | ts.TypeFlags.Boolean | ts.TypeFlags.EnumLiteral | ts.TypeFlags.BigInt | ts.TypeFlags.ESSymbol)) !== 0
45+
) {
46+
discoveredVariant = Variant.PRIMITIVE;
47+
} else if (type.isClassOrInterface() || (type.getFlags() & ts.TypeFlags.Object) !== 0) {
48+
// Check if it's literally just a type alias to something else or a complex object
49+
if (type.getProperties().length > 0) {
50+
discoveredVariant = Variant.OBJECT;
51+
} else {
52+
discoveredVariant = Variant.TYPE;
53+
}
54+
} else {
55+
discoveredVariant = Variant.TYPE;
56+
}
57+
}
58+
ts.forEachChild(node, visit);
59+
};
60+
61+
visit(sourceFile);
62+
return discoveredVariant;
63+
};

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from './getNodeSuggestions';
44
export * from './getNodeValidation';
55
export * from './getReferenceSuggestions';
66
export * from './getTypeFromValue';
7+
export * from './getTypeVariant';
78
export * from './getValueFromType';
89
export * from './getValueSuggestions';
910
export * from './getValueValidation';

test/getTypeVariant.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getTypeVariant, Variant } from '../src/getTypeVariant';
3+
import { DATA_TYPES } from "../src/data";
4+
5+
describe('getTypeVariant', () => {
6+
it('sollte PRIMITIVE für einfache Typen zurückgeben', () => {
7+
expect(getTypeVariant("string", DATA_TYPES)).toBe(Variant.PRIMITIVE);
8+
expect(getTypeVariant("number", DATA_TYPES)).toBe(Variant.PRIMITIVE);
9+
expect(getTypeVariant("boolean", DATA_TYPES)).toBe(Variant.PRIMITIVE);
10+
});
11+
12+
it('sollte ARRAY für Array-Typen zurückgeben', () => {
13+
expect(getTypeVariant("string[]", DATA_TYPES)).toBe(Variant.ARRAY);
14+
expect(getTypeVariant("Array<number>", DATA_TYPES)).toBe(Variant.ARRAY);
15+
});
16+
17+
it('sollte OBJECT für Interfaces oder Objekte mit Properties zurückgeben', () => {
18+
expect(getTypeVariant("{ name: string }", DATA_TYPES)).toBe(Variant.OBJECT);
19+
});
20+
21+
it('sollte TYPE für einfache Type-Aliase oder void zurückgeben', () => {
22+
expect(getTypeVariant("void", DATA_TYPES)).toBe(Variant.TYPE);
23+
expect(getTypeVariant("any", DATA_TYPES)).toBe(Variant.TYPE);
24+
});
25+
26+
it('sollte LIST (NUMBER) als ARRAY erkennen (wenn in DATA_TYPES definiert)', () => {
27+
// In data.ts ist LIST als T[] definiert
28+
expect(getTypeVariant("LIST<NUMBER>", DATA_TYPES)).toBe(Variant.ARRAY);
29+
});
30+
});
31+

0 commit comments

Comments
 (0)