11import ts from "typescript" ;
2- import { createCompilerHost } from "../utils" ;
3- import { LiteralValue } from "@code0-tech/sagittarius-graphql-types" ;
2+ import { createCompilerHost , getSharedTypeDeclarations } from "../utils" ;
3+ import { DataType , LiteralValue } from "@code0-tech/sagittarius-graphql-types" ;
44
55/**
66 * Uses the TypeScript compiler to generate a precise type string from any runtime value.
77 */
88export const getTypeFromValue = (
9- value ?: LiteralValue | null
9+ value ?: LiteralValue | null ,
10+ dataTypes ?: DataType [ ]
1011) : string => {
1112 // 1. Serialize value to a JSON string for embedding in source code.
1213 const literal = JSON . stringify ( value ?. value ) ;
1314
15+ if ( ! literal ) return "any"
16+
1417 // 2. Wrap value in virtual source code.
15- const sourceCode = `const tempValue = ${ literal ?? "any" } ;` ;
18+ const sourceCode = `
19+ ${ getSharedTypeDeclarations ( dataTypes , "unknown" ) }
20+ const tempValue = ${ literal ?? "any" } ;
21+ ` ;
1622 const fileName = "index.ts" ;
1723 const host = createCompilerHost ( fileName , sourceCode ) ;
1824 const sourceFile = host . getSourceFile ( fileName ) ! ;
@@ -25,16 +31,64 @@ export const getTypeFromValue = (
2531 const visit = ( node : ts . Node ) => {
2632 if ( ts . isVariableDeclaration ( node ) && node . name . getText ( ) === "tempValue" ) {
2733 const type = checker . getTypeAtLocation ( node ) ;
28- inferredType = checker . typeToString (
29- type ,
30- node ,
31- ts . TypeFormatFlags . NoTruncation | ts . TypeFormatFlags . UseFullyQualifiedType
32- ) ;
34+ const allAliases = checker . getSymbolsInScope ( node , ts . SymbolFlags . TypeAlias ) ;
35+
36+ const resolve = ( t : ts . Type ) : string => {
37+ if ( t . isUnion ( ) ) {
38+ const parts = t . types . map ( resolve ) ;
39+ return Array . from ( new Set ( parts ) ) . sort ( ) . join ( " | " ) ;
40+ }
41+
42+ // 2. Array-Handling
43+ if ( checker . isArrayType ( t ) ) {
44+ const typeArgs = ( t as any ) . typeArguments || [ ] ;
45+ const inner = typeArgs . length > 0 ? resolve ( typeArgs [ 0 ] ) : "any" ;
46+ return typeArgs [ 0 ] ?. isUnion ( ) ? `(${ inner } )[]` : `${ inner } []` ;
47+ }
48+
49+ if ( ( t . getFlags ( ) & ts . TypeFlags . Object ) || t . isClassOrInterface ( ) ) {
50+ const props = checker . getPropertiesOfType ( t ) ;
51+ if ( props . length > 0 ) {
52+ const fields = props . map ( p => {
53+ const pType = checker . getTypeOfSymbolAtLocation ( p , node ) ;
54+ return `${ p . getName ( ) } : ${ resolve ( pType ) } ` ;
55+ } ) ;
56+ return `{ ${ fields . join ( ", " ) } }` ;
57+ }
58+ }
59+
60+ // 4. Alias-Suche für Blätter (Zahlen, Strings, etc.)
61+ const matches = allAliases . filter ( s =>
62+ checker . isTypeAssignableTo ( t , checker . getDeclaredTypeOfSymbol ( s ) )
63+ ) ;
64+
65+ if ( matches . length > 0 ) {
66+ const bestMatch = matches . sort ( ( a , b ) => {
67+ const typeA = checker . getDeclaredTypeOfSymbol ( a ) ;
68+ const typeB = checker . getDeclaredTypeOfSymbol ( b ) ;
69+
70+ const aSubB = checker . isTypeAssignableTo ( typeA , typeB ) ;
71+ const bSubA = checker . isTypeAssignableTo ( typeB , typeA ) ;
72+
73+ if ( aSubB && ! bSubA ) return - 1 ;
74+ if ( bSubA && ! aSubB ) return 1 ;
75+
76+ return a . getName ( ) . length - b . getName ( ) . length || a . getName ( ) . localeCompare ( b . getName ( ) ) ;
77+ } ) [ 0 ] ;
78+
79+ return bestMatch . getName ( ) ;
80+ }
81+
82+ return checker . typeToString ( t , node ) ;
83+ } ;
84+
85+ inferredType = resolve ( type ) ;
3386 }
3487 ts . forEachChild ( node , visit ) ;
3588 } ;
3689
3790 visit ( sourceFile ) ;
3891
3992 return inferredType ;
40- } ;
93+ } ;
94+
0 commit comments