@@ -381,15 +381,13 @@ export function processVariableDeclaration(decl: Declaration): string {
381381 // If we have a value, check if it has 'as const' - if so, infer from value instead of type annotation
382382 if ( decl . value && decl . value . includes ( 'as const' ) ) {
383383 typeAnnotation = inferNarrowType ( decl . value , true )
384- } else if ( decl . value && kind === 'const' ) {
385- // For const declarations, always try to infer a more specific type from the value
386- const inferredType = inferNarrowType ( decl . value , false )
387-
388- // Use the inferred type if it's more specific than a generic Record type
389- if ( ! typeAnnotation ||
390- typeAnnotation . startsWith ( 'Record<' ) ||
391- typeAnnotation === 'any' ||
392- typeAnnotation === 'object' ) {
384+ } else if ( ! typeAnnotation && decl . value && kind === 'const' ) {
385+ // For const declarations WITHOUT explicit type annotation, infer narrow types from the value
386+ typeAnnotation = inferNarrowType ( decl . value , true )
387+ } else if ( typeAnnotation && decl . value && kind === 'const' && isGenericType ( typeAnnotation ) ) {
388+ // For const declarations with generic type annotations (Record, any, object), prefer narrow inference
389+ const inferredType = inferNarrowType ( decl . value , true )
390+ if ( inferredType !== 'unknown' ) {
393391 typeAnnotation = inferredType
394392 }
395393 } else if ( ! typeAnnotation && decl . value ) {
@@ -755,20 +753,14 @@ export function inferNarrowType(value: any, isConst: boolean = false): string {
755753 return 'string'
756754 }
757755
758- // Number literals
756+ // Number literals - ALWAYS use literal types for const declarations
759757 if ( / ^ - ? \d + ( \. \d + ) ? $ / . test ( trimmed ) ) {
760- if ( isConst ) {
761- return trimmed
762- }
763- return 'number'
758+ return trimmed // Always return literal number
764759 }
765760
766- // Boolean literals
761+ // Boolean literals - ALWAYS use literal types for const declarations
767762 if ( trimmed === 'true' || trimmed === 'false' ) {
768- if ( isConst ) {
769- return trimmed
770- }
771- return 'boolean'
763+ return trimmed // Always return literal boolean
772764 }
773765
774766 // Null and undefined
@@ -975,12 +967,24 @@ function inferArrayType(value: string, isConst: boolean): string {
975967 return inferNarrowTypeInUnion ( trimmedEl , isConst )
976968 } )
977969
978- // For const arrays, create readonly tuples instead of union types
970+ // For const arrays, ALWAYS create readonly tuples for better type safety
979971 if ( isConst ) {
980972 return `readonly [${ elementTypes . join ( ', ' ) } ]`
981973 }
982974
975+ // For simple arrays with all same literal types, also create tuples
983976 const uniqueTypes = [ ...new Set ( elementTypes ) ]
977+ const allLiterals = elementTypes . every ( type =>
978+ / ^ - ? \d + ( \. \d + ) ? $ / . test ( type ) || // numbers
979+ type === 'true' || type === 'false' || // booleans
980+ ( type . startsWith ( '"' ) && type . endsWith ( '"' ) ) || // strings
981+ ( type . startsWith ( "'" ) && type . endsWith ( "'" ) )
982+ )
983+
984+ if ( allLiterals && elementTypes . length <= 10 ) {
985+ // Create tuple for small arrays with literal types
986+ return `readonly [${ elementTypes . join ( ', ' ) } ]`
987+ }
984988
985989 if ( uniqueTypes . length === 1 ) {
986990 return `Array<${ uniqueTypes [ 0 ] } >`
@@ -1044,14 +1048,48 @@ function inferObjectType(value: string, isConst: boolean): string {
10441048 const properties = parseObjectProperties ( content )
10451049 const propTypes : string [ ] = [ ]
10461050
1047- for ( const [ key , val ] of properties ) {
1048- const valueType = inferNarrowType ( val , isConst )
1051+ for ( const [ key , val ] of properties ) {
1052+ let valueType = inferNarrowType ( val , isConst )
1053+
1054+ // Handle method signatures - clean up async and parameter defaults
1055+ if ( valueType . includes ( '=>' ) || valueType . includes ( 'function' ) || valueType . includes ( 'async' ) ) {
1056+ valueType = cleanMethodSignature ( valueType )
1057+ }
1058+
10491059 propTypes . push ( `${ key } : ${ valueType } ` )
10501060 }
10511061
10521062 return `{\n ${ propTypes . join ( ';\n ' ) } \n}`
10531063}
10541064
1065+ /**
1066+ * Clean method signatures for declaration files
1067+ */
1068+ function cleanMethodSignature ( signature : string ) : string {
1069+ // Remove async modifier from method signatures (including in object methods)
1070+ let cleaned = signature . replace ( / ^ a s y n c \s + / , '' ) . replace ( / \b a s y n c \s + / g, '' )
1071+
1072+ // Remove parameter default values (e.g., currency = 'USD' becomes currency?)
1073+ cleaned = cleaned . replace ( / ( \w + ) \s * = \s * [ ^ , ) ] + / g, ( match , paramName ) => {
1074+ return `${ paramName } ?`
1075+ } )
1076+
1077+ // Clean up extra spaces
1078+ cleaned = cleaned . replace ( / \s + / g, ' ' ) . trim ( )
1079+
1080+ return cleaned
1081+ }
1082+
1083+ /**
1084+ * Clean parameter defaults from function parameters
1085+ */
1086+ function cleanParameterDefaults ( params : string ) : string {
1087+ // Remove parameter default values and make them optional
1088+ return params . replace ( / ( \w + ) \s * = \s * [ ^ , ) ] + / g, ( match , paramName ) => {
1089+ return `${ paramName } ?`
1090+ } )
1091+ }
1092+
10551093/**
10561094 * Parse object properties
10571095 */
@@ -1086,9 +1124,30 @@ function parseObjectProperties(content: string): Array<[string, string]> {
10861124 currentKey = current . trim ( )
10871125 current = ''
10881126 inKey = false
1127+ } else if ( char === '(' && depth === 0 && inKey ) {
1128+ // This might be a method definition like: methodName(params) or async methodName<T>(params)
1129+ currentKey = current . trim ( )
1130+ // Remove 'async' from the key if present
1131+ if ( currentKey . startsWith ( 'async ' ) ) {
1132+ currentKey = currentKey . slice ( 6 ) . trim ( )
1133+ }
1134+ current = char // Start with the opening parenthesis
1135+ inKey = false
1136+ depth = 1 // We're now inside the method definition
10891137 } else if ( char === ',' && depth === 0 ) {
1090- if ( currentKey && current . trim ( ) ) {
1091- properties . push ( [ currentKey , current . trim ( ) ] )
1138+ if ( currentKey && current . trim ( ) ) {
1139+ // Clean method signatures before storing
1140+ let value = current . trim ( )
1141+
1142+ // Check if this is a method definition (starts with parentheses)
1143+ if ( value . startsWith ( '(' ) ) {
1144+ // This is a method definition like: (params): ReturnType { ... }
1145+ value = convertMethodToFunctionType ( currentKey , value )
1146+ } else if ( value . includes ( '=>' ) || value . includes ( 'function' ) || value . includes ( 'async' ) ) {
1147+ value = cleanMethodSignature ( value )
1148+ }
1149+
1150+ properties . push ( [ currentKey , value ] )
10921151 }
10931152 current = ''
10941153 currentKey = ''
@@ -1101,14 +1160,64 @@ function parseObjectProperties(content: string): Array<[string, string]> {
11011160 }
11021161 }
11031162
1104- // Don't forget the last property
1163+ // Don't forget the last property
11051164 if ( currentKey && current . trim ( ) ) {
1106- properties . push ( [ currentKey , current . trim ( ) ] )
1165+ let value = current . trim ( )
1166+
1167+ // Check if this is a method definition (starts with parentheses)
1168+ if ( value . startsWith ( '(' ) ) {
1169+ // This is a method definition like: (params): ReturnType { ... }
1170+ value = convertMethodToFunctionType ( currentKey , value )
1171+ } else if ( value . includes ( '=>' ) || value . includes ( 'function' ) || value . includes ( 'async' ) ) {
1172+ value = cleanMethodSignature ( value )
1173+ }
1174+
1175+ properties . push ( [ currentKey , value ] )
11071176 }
11081177
11091178 return properties
11101179}
11111180
1181+ /**
1182+ * Convert method definition to function type signature
1183+ */
1184+ function convertMethodToFunctionType ( methodName : string , methodDef : string ) : string {
1185+ // Remove async modifier if present
1186+ let cleaned = methodDef . replace ( / ^ a s y n c \s + / , '' )
1187+
1188+ // Extract generics, parameters, and return type
1189+ const genericMatch = cleaned . match ( / ^ < ( [ ^ > ] + ) > / )
1190+ const generics = genericMatch ? genericMatch [ 0 ] : ''
1191+ if ( generics ) {
1192+ cleaned = cleaned . slice ( generics . length ) . trim ( )
1193+ }
1194+
1195+ // Find parameter list
1196+ const paramStart = cleaned . indexOf ( '(' )
1197+ const paramEnd = findMatchingBracket ( cleaned , paramStart , '(' , ')' )
1198+
1199+ if ( paramStart === - 1 || paramEnd === - 1 ) {
1200+ return '() => unknown'
1201+ }
1202+
1203+ const params = cleaned . slice ( paramStart , paramEnd + 1 )
1204+ let returnType = 'unknown'
1205+
1206+ // Check for explicit return type annotation
1207+ const afterParams = cleaned . slice ( paramEnd + 1 ) . trim ( )
1208+ if ( afterParams . startsWith ( ':' ) ) {
1209+ const returnTypeMatch = afterParams . match ( / ^ : \s * ( [ ^ { ] + ) / )
1210+ if ( returnTypeMatch ) {
1211+ returnType = returnTypeMatch [ 1 ] . trim ( )
1212+ }
1213+ }
1214+
1215+ // Clean parameter defaults
1216+ const cleanedParams = cleanParameterDefaults ( params )
1217+
1218+ return `${ generics } ${ cleanedParams } => ${ returnType } `
1219+ }
1220+
11121221/**
11131222 * Find matching bracket for nested structures
11141223 */
@@ -1134,24 +1243,10 @@ function inferFunctionType(value: string, inUnion: boolean = false): string {
11341243 const trimmed = value . trim ( )
11351244
11361245 // Handle very complex function types early (but not function expressions)
1137- if ( ( trimmed . length > 100 || ( trimmed . match ( / = > / g) || [ ] ) . length > 2 ) && ! trimmed . startsWith ( 'function' ) ) {
1138- // Extract just the basic signature pattern
1139- const genericMatch = trimmed . match ( / ^ < [ ^ > ] + > / )
1140- const generics = genericMatch ? genericMatch [ 0 ] : ''
1141-
1142- // Look for first parameter pattern - need to find the complete parameter list
1143- let paramStart = trimmed . indexOf ( '(' )
1144- if ( paramStart !== - 1 ) {
1145- let paramEnd = findMatchingBracket ( trimmed , paramStart , '(' , ')' )
1146- if ( paramEnd !== - 1 ) {
1147- const params = trimmed . substring ( paramStart , paramEnd + 1 )
1148- const funcType = `${ generics } ${ params } => any`
1149- return inUnion ? `(${ funcType } )` : funcType
1150- }
1151- }
1152-
1153- // Fallback if parameter extraction fails
1154- const funcType = `${ generics } (...args: any[]) => any`
1246+ // Only simplify if it's truly complex AND looks like a problematic signature
1247+ if ( trimmed . length > 200 && ( trimmed . match ( / = > / g) || [ ] ) . length > 2 && ( trimmed . match ( / < / g) || [ ] ) . length > 5 && ! trimmed . startsWith ( 'function' ) ) {
1248+ // For extremely complex types, use a simple signature
1249+ const funcType = '(...args: any[]) => any'
11551250 return inUnion ? `(${ funcType } )` : funcType
11561251 }
11571252
@@ -1162,6 +1257,9 @@ function inferFunctionType(value: string, inUnion: boolean = false): string {
11621257 let params = asyncRemoved . substring ( 0 , arrowIndex ) . trim ( )
11631258 let body = asyncRemoved . substring ( arrowIndex + 2 ) . trim ( )
11641259
1260+ // Clean up params - remove default values
1261+ params = cleanParameterDefaults ( params )
1262+
11651263 // Clean up params
11661264 if ( params === '()' || params === '' ) {
11671265 params = '()'
@@ -1218,6 +1316,9 @@ function inferFunctionType(value: string, inUnion: boolean = false): string {
12181316 params = params . substring ( 0 , params . lastIndexOf ( '):' ) ) + ')'
12191317 }
12201318
1319+ // Clean up params - remove default values
1320+ params = cleanParameterDefaults ( params )
1321+
12211322 // Clean up params
12221323 if ( params === '()' || params === '' ) {
12231324 params = '()'
0 commit comments