goscript 0.0.22 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,8 @@ import (
15
15
  // - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
16
16
  // Values are processed by `writeBoxedValue`.
17
17
  // - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
18
- // Translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
18
+ // - For `[]byte{...}`, translated to `new Uint8Array([...])`.
19
+ // - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
19
20
  // It handles both keyed and unkeyed elements, infers length if necessary,
20
21
  // and uses zero values for uninitialized array elements.
21
22
  // Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
@@ -73,14 +74,29 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
73
74
  // We'll handle this with depth parameter to arrayToSlice
74
75
  }
75
76
 
76
- c.tsw.WriteLiterally("$.arrayToSlice")
77
+ // Check if it's a []byte literal
78
+ isByteSliceLiteral := false
79
+ if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
80
+ if sliceT, ok := typInfo.Underlying().(*types.Slice); ok {
81
+ if basicElem, ok := sliceT.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
82
+ isByteSliceLiteral = true
83
+ }
84
+ }
85
+ }
77
86
 
78
- // write the type annotation
79
- c.tsw.WriteLiterally("<")
80
- // Write the element type using the existing function
81
- c.WriteTypeExpr(arrType.Elt)
82
- c.tsw.WriteLiterally(">")
87
+ if isByteSliceLiteral {
88
+ c.tsw.WriteLiterally("new Uint8Array")
89
+ } else {
90
+ c.tsw.WriteLiterally("$.arrayToSlice")
91
+
92
+ // write the type annotation
93
+ c.tsw.WriteLiterally("<")
94
+ // Write the element type using the existing function
95
+ c.WriteTypeExpr(arrType.Elt)
96
+ c.tsw.WriteLiterally(">")
97
+ }
83
98
 
99
+ // opening
84
100
  c.tsw.WriteLiterally("([")
85
101
 
86
102
  // Use type info to get array length and element type
@@ -166,7 +182,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
166
182
  c.tsw.WriteLiterally("]")
167
183
 
168
184
  // If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
169
- if isMultiDimensional {
185
+ if isMultiDimensional && !isByteSliceLiteral { // Depth parameter not applicable to Uint8Array constructor
170
186
  c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
171
187
  }
172
188
 
@@ -271,9 +287,8 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
271
287
  }
272
288
 
273
289
  // Handle the case where an anonymous struct has values without keys
274
- // Handle the case where an anonymous struct has values without keys.
275
290
  // This block processes non-key-value elements and associates them with struct fields.
276
- if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 && structType != nil {
291
+ if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
277
292
  // Check if any elements in the composite literal are not key-value pairs.
278
293
  hasNonKeyValueElts := false
279
294
  for _, elt := range exp.Elts {
package/compiler/decl.go CHANGED
@@ -88,14 +88,50 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
88
88
  return fmt.Errorf("failed to write function name: %w", err)
89
89
  }
90
90
 
91
+ // Write type parameters if present
92
+ if decl.Type.TypeParams != nil {
93
+ c.WriteTypeParameters(decl.Type.TypeParams)
94
+ }
95
+
91
96
  // WriteFuncType needs to be aware if the function is async
92
97
  c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
93
98
  c.tsw.WriteLiterally(" ")
94
99
 
100
+ hasNamedReturns := false
101
+ if decl.Type.Results != nil {
102
+ for _, field := range decl.Type.Results.List {
103
+ if len(field.Names) > 0 {
104
+ hasNamedReturns = true
105
+ break
106
+ }
107
+ }
108
+ }
109
+
110
+ if hasNamedReturns {
111
+ c.tsw.WriteLine("{")
112
+ c.tsw.Indent(1)
113
+
114
+ // Declare named return variables and initialize them to their zero values
115
+ for _, field := range decl.Type.Results.List {
116
+ for _, name := range field.Names {
117
+ c.tsw.WriteLiterallyf("let %s: ", name.Name)
118
+ c.WriteTypeExpr(field.Type)
119
+ c.tsw.WriteLiterally(" = ")
120
+ c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
121
+ c.tsw.WriteLine("")
122
+ }
123
+ }
124
+ }
125
+
95
126
  if err := c.WriteStmt(decl.Body); err != nil {
96
127
  return fmt.Errorf("failed to write function body: %w", err)
97
128
  }
98
129
 
130
+ if hasNamedReturns {
131
+ c.tsw.Indent(-1)
132
+ c.tsw.WriteLine("}")
133
+ }
134
+
99
135
  return nil
100
136
  }
101
137
 
@@ -13,15 +13,19 @@ import (
13
13
  // into its TypeScript equivalent.
14
14
  // It handles several Go built-in functions specially:
15
15
  // - `println(...)` becomes `console.log(...)`.
16
+ // - `panic(...)` becomes `$.panic(...)`.
16
17
  // - `len(arg)` becomes `$.len(arg)`.
17
18
  // - `cap(arg)` becomes `$.cap(arg)`.
18
19
  // - `delete(m, k)` becomes `$.deleteMapEntry(m, k)`.
19
20
  // - `make(chan T, size)` becomes `$.makeChannel<T_ts>(size, zeroValueForT)`.
20
21
  // - `make(map[K]V)` becomes `$.makeMap<K_ts, V_ts>()`.
21
22
  // - `make([]T, len, cap)` becomes `$.makeSlice<T_ts>(len, cap)`.
23
+ // - `make([]byte, len, cap)` becomes `new Uint8Array(len)`.
22
24
  // - `string(runeVal)` becomes `String.fromCharCode(runeVal)`.
23
- // - `string([]runeVal)` or `string([]byteVal)` becomes `$.runesToString(sliceVal)`.
24
- // - `[]rune(stringVal)` becomes `$.stringToRunes(stringVal)`.
25
+ // - `string([]runeVal)` becomes `$.runesToString(sliceVal)`.
26
+ // - `string([]byteVal)` becomes `$.bytesToString(sliceVal)`.
27
+ // - `[]rune(stringVal)` becomes `$.stringToRunes(stringVal)“.
28
+ // - `[]byte(stringVal)` becomes `$.stringToBytes(stringVal)`.
25
29
  // - `close(ch)` becomes `ch.close()`.
26
30
  // - `append(slice, elems...)` becomes `$.append(slice, elems...)`.
27
31
  // - `byte(val)` becomes `$.byte(val)`.
@@ -70,66 +74,56 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
70
74
  }
71
75
  }
72
76
  }
77
+ // Check if it's a []byte type and the argument is a string
78
+ if eltIdent, ok := arrayType.Elt.(*ast.Ident); ok && eltIdent.Name == "byte" && arrayType.Len == nil {
79
+ if len(exp.Args) == 1 {
80
+ arg := exp.Args[0]
81
+ // Ensure TypesInfo is available and the argument type can be determined
82
+ if tv, typeOk := c.pkg.TypesInfo.Types[arg]; typeOk && tv.Type != nil {
83
+ if basicArgType, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basicArgType.Info()&types.IsString) != 0 {
84
+ c.tsw.WriteLiterally("$.stringToBytes(")
85
+ if err := c.WriteValueExpr(arg); err != nil {
86
+ return fmt.Errorf("failed to write argument for []byte(string) conversion: %w", err)
87
+ }
88
+ c.tsw.WriteLiterally(")")
89
+ return nil // Handled []byte(string)
90
+ }
91
+ }
92
+ }
93
+ }
73
94
  }
74
95
 
75
96
  if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
76
97
  switch funIdent.String() {
98
+ case "panic":
99
+ c.tsw.WriteLiterally("$.panic")
77
100
  case "println":
78
- c.tsw.WriteLiterally("console.log(")
79
- for i, arg := range exp.Args {
80
- if i != 0 {
81
- c.tsw.WriteLiterally(", ")
82
- }
83
- if err := c.WriteValueExpr(arg); err != nil {
84
- return err
85
- }
86
- }
87
- c.tsw.WriteLiterally(")")
88
- return nil
101
+ c.tsw.WriteLiterally("console.log")
89
102
  case "len":
90
103
  // Translate len(arg) to $.len(arg)
91
- if len(exp.Args) == 1 {
92
- c.tsw.WriteLiterally("$.len(")
93
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
94
- return err
95
- }
96
- c.tsw.WriteLiterally(")")
97
- return nil // Handled len
104
+ if len(exp.Args) != 1 {
105
+ return errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
98
106
  }
99
- return errors.New("unhandled len call with incorrect number of arguments")
107
+ c.tsw.WriteLiterally("$.len")
100
108
  case "cap":
101
109
  // Translate cap(arg) to $.cap(arg)
102
- if len(exp.Args) == 1 {
103
- c.tsw.WriteLiterally("$.cap(")
104
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
105
- return err
106
- }
107
- c.tsw.WriteLiterally(")")
108
- return nil // Handled cap
110
+ if len(exp.Args) != 1 {
111
+ return errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
109
112
  }
110
- return errors.New("unhandled cap call with incorrect number of arguments")
113
+ c.tsw.WriteLiterally("$.cap")
111
114
  case "delete":
112
115
  // Translate delete(map, key) to $.deleteMapEntry(map, key)
113
- if len(exp.Args) == 2 {
114
- c.tsw.WriteLiterally("$.deleteMapEntry(")
115
- if err := c.WriteValueExpr(exp.Args[0]); err != nil { // Map
116
- return err
117
- }
118
- c.tsw.WriteLiterally(", ")
119
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Key
120
- return err
121
- }
122
- c.tsw.WriteLiterally(")")
123
- return nil // Handled delete
116
+ if len(exp.Args) != 2 {
117
+ return errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
124
118
  }
125
- return errors.New("unhandled delete call with incorrect number of arguments")
119
+ c.tsw.WriteLiterally("$.deleteMapEntry")
126
120
  case "make":
127
121
  // First check if we have a channel type
128
122
  if typ := c.pkg.TypesInfo.TypeOf(exp.Args[0]); typ != nil {
129
123
  if chanType, ok := typ.Underlying().(*types.Chan); ok {
130
124
  // Handle channel creation: make(chan T, bufferSize) or make(chan T)
131
125
  c.tsw.WriteLiterally("$.makeChannel<")
132
- c.WriteGoType(chanType.Elem())
126
+ c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
133
127
  c.tsw.WriteLiterally(">(")
134
128
 
135
129
  // If buffer size is provided, add it
@@ -189,13 +183,30 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
189
183
  if sliceType == nil {
190
184
  return errors.New("could not get type information for slice in make call")
191
185
  }
192
- goElemType, ok := sliceType.Underlying().(*types.Slice)
186
+ goUnderlyingType, ok := sliceType.Underlying().(*types.Slice)
193
187
  if !ok {
194
188
  return errors.New("expected slice type for make call")
195
189
  }
190
+ goElemType := goUnderlyingType.Elem()
191
+
192
+ // Check if it's make([]byte, ...)
193
+ if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
194
+ c.tsw.WriteLiterally("new Uint8Array(")
195
+ if len(exp.Args) >= 2 {
196
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
197
+ return err
198
+ }
199
+ // Capacity argument for make([]byte, len, cap) is ignored for new Uint8Array(len)
200
+ } else {
201
+ // If no length is provided, default to 0
202
+ c.tsw.WriteLiterally("0")
203
+ }
204
+ c.tsw.WriteLiterally(")")
205
+ return nil // Handled make for []byte
206
+ }
196
207
 
197
208
  c.tsw.WriteLiterally("$.makeSlice<")
198
- c.WriteGoType(goElemType.Elem()) // Write the element type
209
+ c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
199
210
  c.tsw.WriteLiterally(">(")
200
211
 
201
212
  if len(exp.Args) >= 2 {
@@ -253,7 +264,16 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
253
264
  // Handle direct string(int32) conversion
254
265
  // This assumes 'rune' is int32
255
266
  if tv, ok := c.pkg.TypesInfo.Types[arg]; ok {
256
- if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.Int32 {
267
+ // Case 3a: Argument is already a string - no-op
268
+ if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
269
+ // Translate string(stringValue) to stringValue (no-op)
270
+ if err := c.WriteValueExpr(arg); err != nil {
271
+ return fmt.Errorf("failed to write argument for string(string) no-op conversion: %w", err)
272
+ }
273
+ return nil // Handled string(string) no-op
274
+ }
275
+
276
+ if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basic.Kind() == types.Int32 || basic.Kind() == types.UntypedRune) {
257
277
  // Translate string(rune_val) to String.fromCharCode(rune_val)
258
278
  c.tsw.WriteLiterally("String.fromCharCode(")
259
279
  if err := c.WriteValueExpr(arg); err != nil {
@@ -266,18 +286,43 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
266
286
  // Case 3: Argument is a slice of runes or bytes string([]rune{...}) or string([]byte{...})
267
287
  if sliceType, isSlice := tv.Type.Underlying().(*types.Slice); isSlice {
268
288
  if basic, isBasic := sliceType.Elem().Underlying().(*types.Basic); isBasic {
269
- // Handle both runes (int32) and bytes (uint8)
270
- if basic.Kind() == types.Int32 || basic.Kind() == types.Uint8 {
271
- // Translate string([]rune) or string([]byte) to $.runesToString(...)
289
+ // Handle string([]byte)
290
+ if basic.Kind() == types.Uint8 {
291
+ c.tsw.WriteLiterally("$.bytesToString(")
292
+ if err := c.WriteValueExpr(arg); err != nil {
293
+ return fmt.Errorf("failed to write argument for string([]byte) conversion: %w", err)
294
+ }
295
+ c.tsw.WriteLiterally(")")
296
+ return nil // Handled string([]byte)
297
+ }
298
+ // Handle both runes (int32)
299
+ if basic.Kind() == types.Int32 {
300
+ // Translate string([]rune) to $.runesToString(...)
272
301
  c.tsw.WriteLiterally("$.runesToString(")
273
302
  if err := c.WriteValueExpr(arg); err != nil {
274
- return fmt.Errorf("failed to write argument for string([]rune/[]byte) conversion: %w", err)
303
+ return fmt.Errorf("failed to write argument for string([]rune) conversion: %w", err)
275
304
  }
276
305
  c.tsw.WriteLiterally(")")
277
- return nil // Handled string([]rune) or string([]byte)
306
+ return nil // Handled string([]rune)
278
307
  }
279
308
  }
280
309
  }
310
+
311
+ // Case 4: Argument is a generic type parameter (e.g., string | []byte)
312
+ if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
313
+ // Check if this is a []byte | string union constraint
314
+ constraint := typeParam.Constraint()
315
+ if constraint != nil {
316
+ // For now, assume any type parameter that could be string or []byte needs the helper
317
+ // This is a heuristic - in the future we could parse the constraint more precisely
318
+ c.tsw.WriteLiterally("$.genericBytesOrStringToString(")
319
+ if err := c.WriteValueExpr(arg); err != nil {
320
+ return fmt.Errorf("failed to write argument for string(generic) conversion: %w", err)
321
+ }
322
+ c.tsw.WriteLiterally(")")
323
+ return nil // Handled string(generic type parameter)
324
+ }
325
+ }
281
326
  }
282
327
  }
283
328
  // Return error for other unhandled string conversions
@@ -352,8 +397,10 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
352
397
  return fmt.Errorf("failed to write argument for type cast: %w", err)
353
398
  }
354
399
 
355
- // Then use the TypeScript "as" operator with the type name
356
- c.tsw.WriteLiterallyf(" as %s)", funIdent.String())
400
+ // Then use the TypeScript "as" operator with the mapped type name
401
+ c.tsw.WriteLiterally(" as ")
402
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
403
+ c.tsw.WriteLiterally(")")
357
404
  return nil // Handled non-function type cast
358
405
  }
359
406
  }
@@ -396,15 +443,24 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
396
443
  return nil // Handled regular function call
397
444
  }
398
445
  } else {
399
- // Not an identifier (e.g., method call on a value)
400
- if err := c.WriteValueExpr(expFun); err != nil {
401
- return fmt.Errorf("failed to write method expression in call: %w", err)
402
- }
446
+ // If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
447
+ if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
448
+ c.tsw.WriteLiterally("(")
449
+ if err := c.WriteValueExpr(expFun); err != nil {
450
+ return fmt.Errorf("failed to write function literal in call: %w", err)
451
+ }
452
+ c.tsw.WriteLiterally(")")
453
+ } else {
454
+ // Not an identifier (e.g., method call on a value)
455
+ if err := c.WriteValueExpr(expFun); err != nil {
456
+ return fmt.Errorf("failed to write method expression in call: %w", err)
457
+ }
403
458
 
404
- if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
405
- if _, ok := funType.Underlying().(*types.Signature); ok {
406
- if _, isNamed := funType.(*types.Named); isNamed {
407
- c.tsw.WriteLiterally("!")
459
+ if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
460
+ if _, ok := funType.Underlying().(*types.Signature); ok {
461
+ if _, isNamed := funType.(*types.Named); isNamed {
462
+ c.tsw.WriteLiterally("!")
463
+ }
408
464
  }
409
465
  }
410
466
  }
@@ -91,8 +91,28 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
91
91
  return fmt.Errorf("failed to write selector base expression: %w", err)
92
92
  }
93
93
 
94
- // Add .
95
- c.tsw.WriteLiterally(".")
94
+ // Add null assertion for selector expressions when accessing fields/methods on nullable types
95
+ // In Go, accessing fields or calling methods on nil pointers/interfaces panics, so we should throw in TypeScript
96
+ baseType := c.pkg.TypesInfo.TypeOf(exp.X)
97
+ if baseType != nil {
98
+ // Check if the base is a pointer type
99
+ if _, isPtr := baseType.(*types.Pointer); isPtr {
100
+ c.tsw.WriteLiterally("!.")
101
+ } else if _, isInterface := baseType.Underlying().(*types.Interface); isInterface {
102
+ // For interface types, add null assertion since interfaces can be nil
103
+ c.tsw.WriteLiterally("!.")
104
+ } else if callExpr, isCall := exp.X.(*ast.CallExpr); isCall {
105
+ // For function calls that return nullable types, add null assertion
106
+ _ = callExpr // Use the variable to avoid unused error
107
+ c.tsw.WriteLiterally("!.")
108
+ } else {
109
+ // Add .
110
+ c.tsw.WriteLiterally(".")
111
+ }
112
+ } else {
113
+ // Add .
114
+ c.tsw.WriteLiterally(".")
115
+ }
96
116
 
97
117
  // Write the field/method name.
98
118
  // Pass 'true' to WriteIdent to potentially add '.value' if the field itself
@@ -22,13 +22,59 @@ import (
22
22
  func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
23
23
  // Get type information for the expression and use WriteGoType
24
24
  typ := c.pkg.TypesInfo.TypeOf(a)
25
- c.WriteGoType(typ)
25
+ c.WriteGoType(typ, GoTypeContextGeneral)
26
26
  }
27
27
 
28
28
  // writeTypeDescription writes the TypeInfo for a type expr.
29
29
  func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
30
30
  switch t := typeExpr.(type) {
31
31
  case *ast.Ident:
32
+ // Resolve the identifier to its type
33
+ goType := c.pkg.TypesInfo.TypeOf(t)
34
+ if goType != nil {
35
+ if namedType, isNamed := goType.(*types.Named); isNamed {
36
+ if sig, isFuncSig := namedType.Underlying().(*types.Signature); isFuncSig {
37
+ // It's a named function type (e.g. type MyFunc func())
38
+ c.tsw.WriteLiterally("{")
39
+ c.tsw.WriteLiterally("kind: $.TypeKind.Function, ")
40
+ c.tsw.WriteLiterallyf("name: '%s'", namedType.Obj().Name()) // Use the original defined name
41
+
42
+ // Add params if present
43
+ if sig.Params() != nil && sig.Params().Len() > 0 {
44
+ c.tsw.WriteLiterally(", params: [")
45
+ for i := 0; i < sig.Params().Len(); i++ {
46
+ if i > 0 {
47
+ c.tsw.WriteLiterally(", ")
48
+ }
49
+ paramVar := sig.Params().At(i)
50
+ c.writeTypeInfoObject(paramVar.Type())
51
+ }
52
+ c.tsw.WriteLiterally("]")
53
+ } else {
54
+ c.tsw.WriteLiterally(", params: []")
55
+ }
56
+
57
+ // Add results if present
58
+ if sig.Results() != nil && sig.Results().Len() > 0 {
59
+ c.tsw.WriteLiterally(", results: [")
60
+ for i := 0; i < sig.Results().Len(); i++ {
61
+ if i > 0 {
62
+ c.tsw.WriteLiterally(", ")
63
+ }
64
+ resultVar := sig.Results().At(i)
65
+ c.writeTypeInfoObject(resultVar.Type())
66
+ }
67
+ c.tsw.WriteLiterally("]")
68
+ } else {
69
+ c.tsw.WriteLiterally(", results: []")
70
+ }
71
+
72
+ c.tsw.WriteLiterally("}")
73
+ return
74
+ }
75
+ }
76
+ }
77
+
32
78
  if isPrimitiveType(t.Name) {
33
79
  if tsType, ok := GoBuiltinToTypescript(t.Name); ok {
34
80
  c.tsw.WriteLiterally("{")
@@ -112,9 +158,16 @@ func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
112
158
  c.tsw.WriteLiterally("kind: $.TypeKind.Function")
113
159
 
114
160
  // Add name if this is a named function type
115
- if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
116
- if named, ok := namedType.(*types.Named); ok {
117
- c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
161
+ // For an anonymous ast.FuncType, typeExpr is the ast.FuncType itself.
162
+ // We need to check if the type information associated with this AST node
163
+ // points to a *types.Named type.
164
+ resolvedGoType := c.pkg.TypesInfo.TypeOf(typeExpr)
165
+ if resolvedGoType != nil {
166
+ if named, ok := resolvedGoType.(*types.Named); ok {
167
+ // Ensure it's actually a function type that's named
168
+ if _, isFuncSig := named.Underlying().(*types.Signature); isFuncSig {
169
+ c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
170
+ }
118
171
  }
119
172
  }
120
173
 
@@ -174,6 +227,80 @@ func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
174
227
  c.tsw.WriteLiterally("'both'")
175
228
  }
176
229
 
230
+ c.tsw.WriteLiterally("}")
231
+ case *ast.InterfaceType:
232
+ // Handle inline interface types like interface{ Method() string }
233
+ c.tsw.WriteLiterally("{")
234
+ c.tsw.WriteLiterally("kind: $.TypeKind.Interface, ")
235
+ c.tsw.WriteLiterally("methods: [")
236
+
237
+ // Add method signatures for each method in the interface
238
+ if t.Methods != nil && t.Methods.List != nil {
239
+ hasMethod := false
240
+ for _, field := range t.Methods.List {
241
+ // Only process method declarations (not embedded interfaces)
242
+ if len(field.Names) > 0 {
243
+ for _, methodName := range field.Names {
244
+ if hasMethod {
245
+ c.tsw.WriteLiterally(", ")
246
+ }
247
+ hasMethod = true
248
+
249
+ // Write method signature in the same format as writeMethodSignatures
250
+ c.tsw.WriteLiterallyf("{ name: '%s', args: [", methodName.Name)
251
+
252
+ // Get the function type for this method
253
+ if funcType, ok := field.Type.(*ast.FuncType); ok {
254
+ // Add parameters
255
+ if funcType.Params != nil && funcType.Params.List != nil {
256
+ paramIndex := 0
257
+ for _, param := range funcType.Params.List {
258
+ // Each parameter field can declare multiple variables of the same type
259
+ if len(param.Names) > 0 {
260
+ for _, paramName := range param.Names {
261
+ if paramIndex > 0 {
262
+ c.tsw.WriteLiterally(", ")
263
+ }
264
+ c.tsw.WriteLiterallyf("{ name: '%s', type: ", paramName.Name)
265
+ c.writeTypeDescription(param.Type)
266
+ c.tsw.WriteLiterally(" }")
267
+ paramIndex++
268
+ }
269
+ } else {
270
+ // No names, create a generic parameter name
271
+ if paramIndex > 0 {
272
+ c.tsw.WriteLiterally(", ")
273
+ }
274
+ c.tsw.WriteLiterallyf("{ name: '_p%d', type: ", paramIndex)
275
+ c.writeTypeDescription(param.Type)
276
+ c.tsw.WriteLiterally(" }")
277
+ paramIndex++
278
+ }
279
+ }
280
+ }
281
+
282
+ c.tsw.WriteLiterally("], returns: [")
283
+
284
+ // Add return types
285
+ if funcType.Results != nil && funcType.Results.List != nil {
286
+ for i, result := range funcType.Results.List {
287
+ if i > 0 {
288
+ c.tsw.WriteLiterally(", ")
289
+ }
290
+ c.tsw.WriteLiterally("{ type: ")
291
+ c.writeTypeDescription(result.Type)
292
+ c.tsw.WriteLiterally(" }")
293
+ }
294
+ }
295
+
296
+ c.tsw.WriteLiterally("] }")
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ c.tsw.WriteLiterally("]")
177
304
  c.tsw.WriteLiterally("}")
178
305
  default:
179
306
  // For other types, use the string representation
@@ -1,6 +1,8 @@
1
1
  package compiler
2
2
 
3
- import "go/ast"
3
+ import (
4
+ "go/ast"
5
+ )
4
6
 
5
7
  // WriteValueExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
6
8
  // that represents a value into its TypeScript value equivalent.
@@ -50,43 +52,11 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
50
52
  return c.WriteTypeAssertExpr(exp)
51
53
  case *ast.IndexExpr:
52
54
  return c.WriteIndexExpr(exp)
55
+ case *ast.IndexListExpr:
56
+ // Handle generic function instantiation: f[T1, T2](args) -> f<T1, T2>(args)
57
+ return c.WriteIndexListExpr(exp)
53
58
  case *ast.SliceExpr:
54
- // Translate Go slice expression to $.goSlice(x, low, high, max)
55
- c.tsw.WriteLiterally("$.goSlice(")
56
- if err := c.WriteValueExpr(exp.X); err != nil {
57
- return err
58
- }
59
- // low argument
60
- c.tsw.WriteLiterally(", ")
61
- if exp.Low != nil {
62
- if err := c.WriteValueExpr(exp.Low); err != nil {
63
- return err
64
- }
65
- } else {
66
- c.tsw.WriteLiterally("undefined")
67
- }
68
- // high argument
69
- c.tsw.WriteLiterally(", ")
70
- if exp.High != nil {
71
- if err := c.WriteValueExpr(exp.High); err != nil {
72
- return err
73
- }
74
- } else {
75
- c.tsw.WriteLiterally("undefined")
76
- }
77
- // max argument (only for full slice expressions)
78
- if exp.Slice3 {
79
- c.tsw.WriteLiterally(", ")
80
- if exp.Max != nil {
81
- if err := c.WriteValueExpr(exp.Max); err != nil {
82
- return err
83
- }
84
- } else {
85
- c.tsw.WriteLiterally("undefined")
86
- }
87
- }
88
- c.tsw.WriteLiterally(")")
89
- return nil
59
+ return c.WriteSliceExpr(exp)
90
60
  case *ast.ParenExpr:
91
61
  // Check if this is a nil pointer to struct type cast: (*struct{})(nil)
92
62
  if starExpr, isStarExpr := exp.X.(*ast.StarExpr); isStarExpr {