1- import ts from "typescript" ;
2- import { DataType , Flow , FunctionDefinition , NodeFunction , NodeParameter } from "@code0-tech/sagittarius-graphql-types" ;
3- import { createCompilerHost , getParameterCode , getSharedTypeDeclarations , } from "../utils" ;
4- import { getNodeValidation } from "../validation/getNodeValidation" ; // Wieder hinzugefügt
1+ import { DataType , Flow , FunctionDefinition , NodeFunction } from "@code0-tech/sagittarius-graphql-types" ;
2+ import { getInferredTypesFromFlow , sanitizeId } from "../utils" ;
53
64export interface NodeTypes {
75 parameters : string [ ] ;
@@ -16,73 +14,71 @@ export const getTypesFromNode = (
1614 functions ?: FunctionDefinition [ ] ,
1715 dataTypes ?: DataType [ ]
1816) : NodeTypes => {
19- const funcMap = new Map ( functions ?. map ( f => [ f . identifier , f ] ) ) ;
20- const funcDef = funcMap . get ( node ?. functionDefinition ?. identifier ) ;
17+ if ( ! node ) return { parameters : [ ] , returnType : "any" } ;
2118
22- if ( ! funcDef ) {
23- return {
24- parameters : [ ] ,
25- returnType : "any" ,
26- } ;
27- }
19+ const nodeId = node . id || "temp_node_id" ;
2820
29- const mockFlow : Flow = {
30- id : "gid://sagittarius/Flow/0" as any ,
31- nodes : { __typename : "NodeFunctionConnection" , nodes : [ node ] }
32- } as Flow ;
33-
34- const params = ( node ?. parameters ?. nodes as NodeParameter [ ] ) || [ ] ;
35- const paramCodes = params . map ( param => getParameterCode ( param , mockFlow , ( f , n ) => getNodeValidation ( f , n , functions , dataTypes ) ) ) ;
21+ // To ensure generics and triangulated types are resolved without hardcoding,
22+ // we must ensure the compiler has enough context.
23+ // If some parameters are missing values, the compiler might return 'any'.
24+ // We create a version of the node where missing values are filled with a marker
25+ // that the TypeScript engine can use to infer the intended type from the signature.
3626
37- const funcCallArgs = paramCodes . map ( code => code === 'undefined' ? '({} as any)' : code ) . join ( ", " ) ;
27+ const nodeWithDefaults = {
28+ ...node ,
29+ id : nodeId ,
30+ parameters : {
31+ ...node . parameters ,
32+ nodes : node . parameters ?. nodes ?. map ( p => {
33+ if ( p ?. value ) return p ;
34+ // If value is missing, we still want the compiler to see the argument position
35+ return { ...p , value : null } ;
36+ } ) || [ ]
37+ }
38+ } ;
3839
39- const signature = funcDef . signature ;
40- const sourceCode = `
41- ${ getSharedTypeDeclarations ( dataTypes ) }
42- declare function testFunc${ signature } ;
43- const result = testFunc(${ funcCallArgs } );
44- ` ;
40+ // Create a version of the node with primitive literals removed
41+ // This allows inferring the "expected" type of parameters (e.g. keyof T)
42+ // rather than the specific type of the argument provided (e.g. "id").
43+ const nodeIdParams = nodeId + "_params" ;
44+ const nodeForParams = {
45+ ...nodeWithDefaults ,
46+ id : nodeIdParams ,
47+ parameters : {
48+ ...nodeWithDefaults . parameters ,
49+ nodes : nodeWithDefaults . parameters . nodes . map ( p => {
50+ // If it's a primitive literal, remove it to allow wider type inference for parameters
51+ if ( p . value ?. __typename === "LiteralValue" && p . value . value !== null && typeof p . value . value !== 'object' ) {
52+ return { ...p , value : null } ;
53+ }
54+ return p ;
55+ } )
56+ }
57+ } ;
4558
46- const fileName = "index.ts" ;
47- const host = createCompilerHost ( fileName , sourceCode ) ;
48- const sourceFile = host . getSourceFile ( fileName ) ! ;
49- const program = host . languageService . getProgram ( ) ! ;
50- const checker = program . getTypeChecker ( ) ;
59+ const mockFlow : Flow = {
60+ id : "gid://sagittarius/Flow/0" as any ,
61+ nodes : { __typename : "NodeFunctionConnection" , nodes : [ nodeWithDefaults , nodeForParams ] }
62+ } as Flow ;
5163
52- let inferredReturnType = "any" ;
53- let inferredParameterTypes : string [ ] = [ ] ;
64+ const inferred = getInferredTypesFromFlow ( mockFlow , functions , dataTypes ) ;
65+ const sId = sanitizeId ( nodeId ) ;
66+ const sIdParams = sanitizeId ( nodeIdParams ) ;
5467
55- const visitor = ( n : ts . Node ) => {
56- if ( ts . isVariableDeclaration ( n ) && n . name . getText ( ) === "result" ) {
57- const resultType = checker . getTypeAtLocation ( n ) ;
58- inferredReturnType = checker . typeToString (
59- resultType ,
60- n ,
61- ts . TypeFormatFlags . NoTruncation
62- ) ;
68+ const directParams = inferred . parameters . get ( sId ) || [ ] ;
69+ const widenedParams = inferred . parameters . get ( sIdParams ) || [ ] ;
6370
64- if ( ts . isCallExpression ( n . initializer ! ) ) {
65- const callExpr = n . initializer ;
66- const signature = checker . getResolvedSignature ( callExpr ) ;
67- if ( signature ) {
68- inferredParameterTypes = signature . getParameters ( ) . map ( p => {
69- const type = checker . getTypeOfSymbolAtLocation ( p , callExpr ) ;
70- return checker . typeToString (
71- type ,
72- callExpr ,
73- ts . TypeFormatFlags . NoTruncation
74- ) ;
75- } ) ;
76- }
77- }
71+ // Merge parameters: prefer widened types unless they failed inference (any/unknown)
72+ const parameters = directParams . map ( ( p , i ) => {
73+ const wide = widenedParams [ i ] ;
74+ if ( wide && wide !== "any" && wide !== "unknown" ) {
75+ return wide ;
7876 }
79- ts . forEachChild ( n , visitor ) ;
80- } ;
81-
82- visitor ( sourceFile ) ;
77+ return p ;
78+ } ) ;
8379
8480 return {
85- parameters : inferredParameterTypes ,
86- returnType : inferredReturnType ,
81+ parameters,
82+ returnType : inferred . nodes . get ( sId ) || "any" ,
8783 } ;
8884} ;
0 commit comments