goscript 0.0.20 → 0.0.22

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.
@@ -0,0 +1,463 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+ "go/types"
8
+ "strings"
9
+ )
10
+
11
+ // writeTypeAssert handles the Go type assertion with comma-ok idiom in an
12
+ // assignment context: `value, ok := interfaceExpr.(AssertedType)` (or with `=`).
13
+ // It translates this to a TypeScript destructuring assignment (or declaration if `tok`
14
+ // is `token.DEFINE` for `:=`) using the `$.typeAssert` runtime helper.
15
+ //
16
+ // The generated TypeScript is:
17
+ // `[let] { value: valueName, ok: okName } = $.typeAssert<AssertedType_ts>(interfaceExpr_ts, 'AssertedTypeName');`
18
+ //
19
+ // - `AssertedType_ts` is the TypeScript translation of `AssertedType`.
20
+ // - `interfaceExpr_ts` is the TypeScript translation of `interfaceExpr`.
21
+ // - `'AssertedTypeName'` is a string representation of the asserted type name,
22
+ // obtained via `getTypeNameString`, used for runtime error messages.
23
+ // - `valueName` and `okName` are the Go variable names from the LHS.
24
+ // - Blank identifiers (`_`) on the LHS are handled by omitting the corresponding
25
+ // property in the destructuring pattern (e.g., `{ ok: okName } = ...` if `value` is blank).
26
+ // - If `tok` is not `token.DEFINE` (i.e., for regular assignment `=`), the entire
27
+ // destructuring assignment is wrapped in parentheses `(...)` to make it a valid
28
+ // expression if needed, though typically assignments are statements.
29
+ //
30
+ // The statement is terminated with a newline.
31
+ func (c *GoToTSCompiler) writeTypeAssert(lhs []ast.Expr, typeAssertExpr *ast.TypeAssertExpr, tok token.Token) error {
32
+ interfaceExpr := typeAssertExpr.X
33
+ assertedType := typeAssertExpr.Type
34
+
35
+ // Unwrap parenthesized expressions to handle cases like r.((<-chan T))
36
+ for {
37
+ if parenExpr, ok := assertedType.(*ast.ParenExpr); ok {
38
+ assertedType = parenExpr.X
39
+ } else {
40
+ break
41
+ }
42
+ }
43
+
44
+ // Ensure LHS has exactly two expressions (value and ok)
45
+ if len(lhs) != 2 {
46
+ return fmt.Errorf("type assertion assignment requires exactly 2 variables on LHS, got %d", len(lhs))
47
+ }
48
+
49
+ // Get variable names, handling blank identifiers
50
+ valueIsBlank := false
51
+ okIsBlank := false
52
+ var valueName string
53
+ var okName string
54
+ var valueIdent *ast.Ident
55
+ var okIdent *ast.Ident
56
+
57
+ if valId, ok := lhs[0].(*ast.Ident); ok {
58
+ valueIdent = valId
59
+ if valId.Name == "_" {
60
+ valueIsBlank = true
61
+ } else {
62
+ valueName = valId.Name
63
+ }
64
+ } else {
65
+ return fmt.Errorf("unhandled LHS expression type for value in type assertion: %T", lhs[0])
66
+ }
67
+
68
+ if okId, ok := lhs[1].(*ast.Ident); ok {
69
+ okIdent = okId
70
+ if okId.Name == "_" {
71
+ okIsBlank = true
72
+ } else {
73
+ okName = okId.Name
74
+ }
75
+ } else {
76
+ return fmt.Errorf("unhandled LHS expression type for ok in type assertion: %T", lhs[1])
77
+ }
78
+
79
+ // For token.DEFINE (:=), we need to check if any of the variables are already declared
80
+ // In Go, := can be used for redeclaration if at least one variable is new
81
+ writeEndParen := false
82
+ if tok == token.DEFINE {
83
+ // Identify which variables are new vs existing
84
+ valueIsNew := true
85
+ okIsNew := true
86
+ anyNewVars := false
87
+ allNewVars := true
88
+
89
+ // Check if variables are already in scope
90
+ if !valueIsBlank {
91
+ if obj := c.pkg.TypesInfo.Uses[valueIdent]; obj != nil {
92
+ // If it's in Uses, it's referenced elsewhere, so it exists
93
+ valueIsNew = false
94
+ allNewVars = false
95
+ }
96
+ if valueIsNew {
97
+ anyNewVars = true
98
+ }
99
+ }
100
+
101
+ if !okIsBlank {
102
+ if obj := c.pkg.TypesInfo.Uses[okIdent]; obj != nil {
103
+ // If it's in Uses, it's referenced elsewhere, so it exists
104
+ okIsNew = false
105
+ allNewVars = false
106
+ }
107
+ if okIsNew {
108
+ anyNewVars = true
109
+ }
110
+ }
111
+
112
+ if allNewVars && anyNewVars {
113
+ c.tsw.WriteLiterally("let ")
114
+ } else if anyNewVars {
115
+ // If only some variables are new, declare them separately
116
+ if !valueIsBlank && valueIsNew {
117
+ c.tsw.WriteLiterally("let ")
118
+ c.tsw.WriteLiterally(valueName)
119
+ // Add type annotation if possible
120
+ if tv, ok := c.pkg.TypesInfo.Types[assertedType]; ok {
121
+ c.tsw.WriteLiterally(": ")
122
+ c.WriteGoType(tv.Type)
123
+ }
124
+ c.tsw.WriteLine("")
125
+ }
126
+ if !okIsBlank && okIsNew {
127
+ c.tsw.WriteLiterally("let ")
128
+ c.tsw.WriteLiterally(okName)
129
+ c.tsw.WriteLiterally(": boolean") // ok is always boolean
130
+ c.tsw.WriteLine("")
131
+ }
132
+ // Use parenthesized destructuring assignment for existing variables
133
+ c.tsw.WriteLiterally(";(")
134
+ writeEndParen = true
135
+ } else {
136
+ // All variables exist, use parenthesized destructuring assignment
137
+ c.tsw.WriteLiterally(";(")
138
+ writeEndParen = true
139
+ }
140
+ } else {
141
+ c.tsw.WriteLiterally("(")
142
+ }
143
+
144
+ c.tsw.WriteLiterally("{ ")
145
+ // Dynamically build the destructuring pattern
146
+ parts := []string{}
147
+ if !valueIsBlank {
148
+ parts = append(parts, fmt.Sprintf("value: %s", valueName))
149
+ }
150
+ if !okIsBlank {
151
+ parts = append(parts, fmt.Sprintf("ok: %s", okName))
152
+ }
153
+ c.tsw.WriteLiterally(strings.Join(parts, ", "))
154
+ c.tsw.WriteLiterally(" } = $.typeAssert<")
155
+
156
+ // Write the asserted type for the generic
157
+ c.WriteTypeExpr(assertedType)
158
+ c.tsw.WriteLiterally(">(")
159
+
160
+ // Write the interface expression
161
+ if err := c.WriteValueExpr(interfaceExpr); err != nil {
162
+ return fmt.Errorf("failed to write interface expression in type assertion call: %w", err)
163
+ }
164
+ c.tsw.WriteLiterally(", ")
165
+
166
+ // Use structured type information for all types
167
+ switch typeExpr := assertedType.(type) {
168
+ case *ast.MapType:
169
+ c.tsw.WriteLiterally("{")
170
+ c.tsw.WriteLiterally("kind: $.TypeKind.Map, ")
171
+
172
+ c.tsw.WriteLiterally("keyType: ")
173
+ if ident, ok := typeExpr.Key.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
174
+ if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
175
+ c.tsw.WriteLiterallyf("'%s'", tsType)
176
+ } else {
177
+ c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
178
+ }
179
+ } else {
180
+ c.writeTypeDescription(typeExpr.Key)
181
+ }
182
+
183
+ c.tsw.WriteLiterally(", ")
184
+
185
+ // Add element type
186
+ c.tsw.WriteLiterally("elemType: ")
187
+ if ident, ok := typeExpr.Value.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
188
+ if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
189
+ c.tsw.WriteLiterallyf("'%s'", tsType)
190
+ } else {
191
+ c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
192
+ }
193
+ } else {
194
+ c.writeTypeDescription(typeExpr.Value)
195
+ }
196
+
197
+ c.tsw.WriteLiterally("}")
198
+ case *ast.ArrayType:
199
+ // Determine if it's a slice or array
200
+ typeKind := "$.TypeKind.Slice"
201
+ if typeExpr.Len != nil {
202
+ typeKind = "$.TypeKind.Array"
203
+ }
204
+
205
+ // Create a type descriptor object
206
+ c.tsw.WriteLiterally("{")
207
+ c.tsw.WriteLiterallyf("kind: %s, ", typeKind)
208
+
209
+ // Add element type
210
+ c.tsw.WriteLiterally("elemType: ")
211
+ if ident, ok := typeExpr.Elt.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
212
+ if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
213
+ c.tsw.WriteLiterallyf("'%s'", tsType)
214
+ } else {
215
+ c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
216
+ }
217
+ } else {
218
+ c.writeTypeDescription(typeExpr.Elt)
219
+ }
220
+
221
+ c.tsw.WriteLiterally("}")
222
+ case *ast.StructType:
223
+ // For struct types, create a type descriptor object
224
+ c.tsw.WriteLiterally("{")
225
+ c.tsw.WriteLiterally("kind: $.TypeKind.Struct")
226
+
227
+ // Get the type name if available
228
+ typeName := c.getTypeNameString(assertedType)
229
+ if typeName != "unknown" {
230
+ c.tsw.WriteLiterallyf(", name: '%s'", typeName)
231
+ }
232
+
233
+ if typeExpr.Fields != nil && typeExpr.Fields.List != nil {
234
+ // Add fields property to provide type information
235
+ c.tsw.WriteLiterally(", fields: {")
236
+
237
+ hasFields := false
238
+ for _, field := range typeExpr.Fields.List {
239
+ if len(field.Names) > 0 {
240
+ for _, name := range field.Names {
241
+ if hasFields {
242
+ c.tsw.WriteLiterally(", ")
243
+ }
244
+ c.tsw.WriteLiterally(fmt.Sprintf("'%s': ", name.Name))
245
+ c.writeTypeDescription(field.Type)
246
+ hasFields = true
247
+ }
248
+ }
249
+ }
250
+
251
+ c.tsw.WriteLiterally("}")
252
+ } else {
253
+ c.tsw.WriteLiterally(", fields: {}")
254
+ }
255
+
256
+ // Add empty methods set to satisfy StructTypeInfo interface
257
+ c.tsw.WriteLiterally(", methods: []")
258
+
259
+ c.tsw.WriteLiterally("}")
260
+ case *ast.InterfaceType:
261
+ c.tsw.WriteLiterally("{")
262
+ c.tsw.WriteLiterally("kind: $.TypeKind.Interface")
263
+
264
+ // Get the type name if available
265
+ typeName := c.getTypeNameString(assertedType)
266
+ if typeName != "unknown" {
267
+ c.tsw.WriteLiterallyf(", name: '%s'", typeName)
268
+ }
269
+
270
+ // Add methods if available
271
+ c.tsw.WriteLiterally(", methods: []")
272
+
273
+ c.tsw.WriteLiterally("}")
274
+ case *ast.StarExpr:
275
+ c.tsw.WriteLiterally("{")
276
+ c.tsw.WriteLiterally("kind: $.TypeKind.Pointer")
277
+
278
+ // Add element type if it's a struct type or named type
279
+ if structType, ok := typeExpr.X.(*ast.StructType); ok {
280
+ c.tsw.WriteLiterally(", elemType: ")
281
+ c.writeTypeDescription(structType)
282
+ } else if ident, ok := typeExpr.X.(*ast.Ident); ok {
283
+ c.tsw.WriteLiterallyf(", elemType: '%s'", ident.Name)
284
+ }
285
+
286
+ c.tsw.WriteLiterally("}")
287
+ case *ast.ChanType:
288
+ c.tsw.WriteLiterally("{")
289
+ c.tsw.WriteLiterally("kind: $.TypeKind.Channel")
290
+
291
+ // Add element type
292
+ c.tsw.WriteLiterally(", elemType: ")
293
+ if ident, ok := typeExpr.Value.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
294
+ if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
295
+ c.tsw.WriteLiterallyf("'%s'", tsType)
296
+ } else {
297
+ c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
298
+ }
299
+ } else {
300
+ c.writeTypeDescription(typeExpr.Value)
301
+ }
302
+
303
+ c.tsw.WriteLiterally(", direction: ")
304
+ switch typeExpr.Dir {
305
+ case ast.SEND:
306
+ c.tsw.WriteLiterally("'send'")
307
+ case ast.RECV:
308
+ c.tsw.WriteLiterally("'receive'")
309
+ case ast.SEND | ast.RECV: // bidirectional
310
+ c.tsw.WriteLiterally("'both'")
311
+ default:
312
+ // This should not happen, but just in case
313
+ c.tsw.WriteLiterally("'both'")
314
+ }
315
+
316
+ c.tsw.WriteLiterally("}")
317
+ case *ast.FuncType:
318
+ // For function types, create a type descriptor object with params and results
319
+ c.tsw.WriteLiterally("{")
320
+ c.tsw.WriteLiterally("kind: $.TypeKind.Function")
321
+
322
+ // Add name if this is a named function type
323
+ if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
324
+ if named, ok := namedType.(*types.Named); ok {
325
+ c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
326
+ }
327
+ }
328
+
329
+ // Add params if present
330
+ if typeExpr.Params != nil && len(typeExpr.Params.List) > 0 {
331
+ c.tsw.WriteLiterally(", params: [")
332
+ for i, param := range typeExpr.Params.List {
333
+ if i > 0 {
334
+ c.tsw.WriteLiterally(", ")
335
+ }
336
+ c.writeTypeDescription(param.Type)
337
+ }
338
+ c.tsw.WriteLiterally("]")
339
+ }
340
+
341
+ // Add results if present
342
+ if typeExpr.Results != nil && len(typeExpr.Results.List) > 0 {
343
+ c.tsw.WriteLiterally(", results: [")
344
+ for i, result := range typeExpr.Results.List {
345
+ if i > 0 {
346
+ c.tsw.WriteLiterally(", ")
347
+ }
348
+ c.writeTypeDescription(result.Type)
349
+ }
350
+ c.tsw.WriteLiterally("]")
351
+ }
352
+
353
+ c.tsw.WriteLiterally("}")
354
+ case *ast.Ident:
355
+ if isPrimitiveType(typeExpr.Name) {
356
+ c.tsw.WriteLiterally("{")
357
+ c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
358
+
359
+ // Use TypeScript equivalent if available
360
+ if tsType, ok := GoBuiltinToTypescript(typeExpr.Name); ok {
361
+ c.tsw.WriteLiterallyf("name: '%s'", tsType) // TODO: use %q?
362
+ } else {
363
+ c.tsw.WriteLiterallyf("name: '%s'", typeExpr.Name)
364
+ }
365
+
366
+ c.tsw.WriteLiterally("}")
367
+ } else {
368
+ // Check if this is a named function type
369
+ isFunctionType := false
370
+ if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
371
+ if named, ok := namedType.(*types.Named); ok {
372
+ if _, ok := named.Underlying().(*types.Signature); ok {
373
+ // This is a named function type, generate a FunctionTypeInfo
374
+ isFunctionType = true
375
+ c.tsw.WriteLiterally("{")
376
+ c.tsw.WriteLiterally("kind: $.TypeKind.Function")
377
+ c.tsw.WriteLiterallyf(", name: '%s'", typeExpr.Name)
378
+
379
+ // Get the underlying signature
380
+ signature := named.Underlying().(*types.Signature)
381
+
382
+ // Add params if present
383
+ params := signature.Params()
384
+ if params != nil && params.Len() > 0 {
385
+ c.tsw.WriteLiterally(", params: [")
386
+ for i := 0; i < params.Len(); i++ {
387
+ if i > 0 {
388
+ c.tsw.WriteLiterally(", ")
389
+ }
390
+ // Use basic type info for parameters
391
+ param := params.At(i)
392
+ c.tsw.WriteLiterally("{")
393
+ c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
394
+
395
+ typeName := param.Type().String()
396
+ if tsType, ok := GoBuiltinToTypescript(typeName); ok {
397
+ c.tsw.WriteLiterallyf("name: '%s'", tsType)
398
+ } else {
399
+ c.tsw.WriteLiterallyf("name: '%s'", typeName)
400
+ }
401
+
402
+ c.tsw.WriteLiterally("}")
403
+ }
404
+ c.tsw.WriteLiterally("]")
405
+ }
406
+
407
+ // Add results if present
408
+ results := signature.Results()
409
+ if results != nil && results.Len() > 0 {
410
+ c.tsw.WriteLiterally(", results: [")
411
+ for i := 0; i < results.Len(); i++ {
412
+ if i > 0 {
413
+ c.tsw.WriteLiterally(", ")
414
+ }
415
+ result := results.At(i)
416
+ c.tsw.WriteLiterally("{")
417
+ c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
418
+
419
+ typeName := result.Type().String()
420
+ if tsType, ok := GoBuiltinToTypescript(typeName); ok {
421
+ c.tsw.WriteLiterallyf("name: '%s'", tsType)
422
+ } else {
423
+ c.tsw.WriteLiterallyf("name: '%s'", typeName)
424
+ }
425
+
426
+ c.tsw.WriteLiterally("}")
427
+ }
428
+ c.tsw.WriteLiterally("]")
429
+ }
430
+
431
+ c.tsw.WriteLiterally("}")
432
+ }
433
+ }
434
+ }
435
+
436
+ // Default case for non-function named types
437
+ if !isFunctionType {
438
+ c.tsw.WriteLiterallyf("'%s'", typeExpr.Name)
439
+ }
440
+ }
441
+ case *ast.SelectorExpr:
442
+ // For imported types like pkg.Type
443
+ if ident, ok := typeExpr.X.(*ast.Ident); ok {
444
+ c.tsw.WriteLiterallyf("'%s.%s'", ident.Name, typeExpr.Sel.Name)
445
+ } else {
446
+ c.tsw.WriteLiterallyf("'%s'", c.getTypeNameString(assertedType))
447
+ }
448
+ default:
449
+ // For other types, use the string name as before
450
+ typeName := c.getTypeNameString(assertedType)
451
+ c.tsw.WriteLiterallyf("'%s'", typeName)
452
+ }
453
+
454
+ c.tsw.WriteLiterally(")")
455
+
456
+ if tok != token.DEFINE || writeEndParen {
457
+ c.tsw.WriteLiterally(")")
458
+ }
459
+
460
+ c.tsw.WriteLine("") // Add newline after the statement
461
+
462
+ return nil
463
+ }
@@ -0,0 +1,141 @@
1
+ package compiler
2
+
3
+ import "go/types"
4
+
5
+ // writeTypeInfoObject writes a TypeScript TypeInfo object literal for a given Go type.
6
+ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
7
+ if typ == nil {
8
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Basic, name: 'any' }") // Or handle as error
9
+ return
10
+ }
11
+
12
+ // If typ is a *types.Named, handle it by reference to break recursion.
13
+ if namedType, ok := typ.(*types.Named); ok {
14
+ if namedType.Obj().Name() == "error" && namedType.Obj().Pkg() == nil { // Check for builtin error
15
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] }")
16
+ } else {
17
+ // For all other named types, output their name as a string literal.
18
+ // This relies on the type being registered elsewhere (e.g., via registerStructType or registerInterfaceType)
19
+ // so the TypeScript runtime can resolve the reference.
20
+ c.tsw.WriteLiterallyf("%q", namedType.Obj().Name())
21
+ }
22
+ return // Return after handling the named type by reference.
23
+ }
24
+
25
+ // If typ is not *types.Named, process its underlying structure.
26
+ underlying := typ.Underlying()
27
+ switch t := underlying.(type) {
28
+ case *types.Basic:
29
+ tsTypeName, _ := GoBuiltinToTypescript(t.Name())
30
+ if tsTypeName == "" {
31
+ tsTypeName = t.Name() // Fallback
32
+ }
33
+ c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", tsTypeName)
34
+ // Note: The original 'case *types.Named:' here for 'underlying' is intentionally omitted.
35
+ // If typ.Underlying() is *types.Named (e.g. type T1 MyInt; type T2 T1;),
36
+ // then writeTypeInfoObject(typ.Underlying()) would be called in some contexts,
37
+ // and that call would handle it via the top-level *types.Named check.
38
+ case *types.Pointer:
39
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Pointer, elemType: ")
40
+ c.writeTypeInfoObject(t.Elem()) // Recursive call
41
+ c.tsw.WriteLiterally(" }")
42
+ case *types.Slice:
43
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Slice, elemType: ")
44
+ c.writeTypeInfoObject(t.Elem()) // Recursive call
45
+ c.tsw.WriteLiterally(" }")
46
+ case *types.Array:
47
+ c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Array, length: %d, elemType: ", t.Len())
48
+ c.writeTypeInfoObject(t.Elem()) // Recursive call
49
+ c.tsw.WriteLiterally(" }")
50
+ case *types.Map:
51
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Map, keyType: ")
52
+ c.writeTypeInfoObject(t.Key()) // Recursive call
53
+ c.tsw.WriteLiterally(", elemType: ")
54
+ c.writeTypeInfoObject(t.Elem()) // Recursive call
55
+ c.tsw.WriteLiterally(" }")
56
+ case *types.Chan:
57
+ dir := "both"
58
+ if t.Dir() == types.SendOnly {
59
+ dir = "send"
60
+ } else if t.Dir() == types.RecvOnly {
61
+ dir = "receive"
62
+ }
63
+ c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Channel, direction: %q, elemType: ", dir)
64
+ c.writeTypeInfoObject(t.Elem()) // Recursive call
65
+ c.tsw.WriteLiterally(" }")
66
+ case *types.Interface: // Anonymous interface or underlying of a non-named type alias
67
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Interface, methods: [")
68
+ var methods []*types.Func
69
+ for i := 0; i < t.NumExplicitMethods(); i++ {
70
+ methods = append(methods, t.ExplicitMethod(i))
71
+ }
72
+ // TODO: Handle embedded methods for anonymous interfaces if needed.
73
+ c.writeMethodSignatures(methods) // Calls writeMethodSignatures -> writeTypeInfoObject
74
+ c.tsw.WriteLiterally("] }")
75
+ case *types.Signature: // Anonymous func type or underlying of a non-named type alias
76
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Function, params: [")
77
+ for i := 0; i < t.Params().Len(); i++ {
78
+ if i > 0 {
79
+ c.tsw.WriteLiterally(", ")
80
+ }
81
+ c.writeTypeInfoObject(t.Params().At(i).Type()) // Recursive call
82
+ }
83
+ c.tsw.WriteLiterally("], results: [")
84
+ for i := 0; i < t.Results().Len(); i++ {
85
+ if i > 0 {
86
+ c.tsw.WriteLiterally(", ")
87
+ }
88
+ c.writeTypeInfoObject(t.Results().At(i).Type()) // Recursive call
89
+ }
90
+ c.tsw.WriteLiterally("] }")
91
+ case *types.Struct: // Anonymous struct or underlying of a non-named type alias
92
+ c.tsw.WriteLiterally("{ kind: $.TypeKind.Struct, fields: {")
93
+ for i := 0; i < t.NumFields(); i++ {
94
+ if i > 0 {
95
+ c.tsw.WriteLiterally(", ")
96
+ }
97
+ field := t.Field(i)
98
+ c.tsw.WriteLiterallyf("%q: ", field.Name())
99
+ c.writeTypeInfoObject(field.Type()) // Recursive call
100
+ }
101
+ c.tsw.WriteLiterally("}, methods: [] }") // Anonymous structs don't have methods in this context
102
+ default:
103
+ // Fallback, e.g. for types whose underlying isn't one of the above like *types.Tuple or other complex cases.
104
+ c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", typ.String()) // Fallback using the type's string representation
105
+ }
106
+ }
107
+
108
+ // writeMethodSignatures writes an array of TypeScript MethodSignature objects.
109
+ func (c *GoToTSCompiler) writeMethodSignatures(methods []*types.Func) {
110
+ firstMethod := true
111
+ for _, method := range methods {
112
+ if !firstMethod {
113
+ c.tsw.WriteLiterally(", ")
114
+ }
115
+ firstMethod = false
116
+
117
+ sig := method.Type().(*types.Signature)
118
+ c.tsw.WriteLiterallyf("{ name: %q, args: [", method.Name())
119
+ for i := 0; i < sig.Params().Len(); i++ {
120
+ if i > 0 {
121
+ c.tsw.WriteLiterally(", ")
122
+ }
123
+ param := sig.Params().At(i)
124
+ c.tsw.WriteLiterallyf("{ name: %q, type: ", param.Name())
125
+ c.writeTypeInfoObject(param.Type())
126
+ c.tsw.WriteLiterally(" }")
127
+ }
128
+ c.tsw.WriteLiterally("], returns: [")
129
+ for i := 0; i < sig.Results().Len(); i++ {
130
+ if i > 0 {
131
+ c.tsw.WriteLiterally(", ")
132
+ }
133
+ result := sig.Results().At(i)
134
+ // Return parameters in Go often don't have names that are relevant for TS signature matching
135
+ c.tsw.WriteLiterally("{ type: ")
136
+ c.writeTypeInfoObject(result.Type())
137
+ c.tsw.WriteLiterally(" }")
138
+ }
139
+ c.tsw.WriteLiterally("] }")
140
+ }
141
+ }