goscript 0.0.21 → 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,556 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/types"
7
+ "sort"
8
+ "strings"
9
+ )
10
+
11
+ // WriteGoType is the main dispatcher for translating Go types to their TypeScript
12
+ // equivalents. It examines the type and delegates to more specialized type writer
13
+ // functions based on the specific Go type encountered.
14
+ //
15
+ // It handles nil types as 'any' with a comment, and dispatches to appropriate
16
+ // type-specific writers for all other recognized Go types.
17
+ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
18
+ if typ == nil {
19
+ c.tsw.WriteLiterally("any")
20
+ c.tsw.WriteCommentInline("nil type")
21
+ return
22
+ }
23
+
24
+ switch t := typ.(type) {
25
+ case *types.Basic:
26
+ c.WriteBasicType(t)
27
+ case *types.Named:
28
+ c.WriteNamedType(t)
29
+ case *types.Pointer:
30
+ c.WritePointerType(t)
31
+ case *types.Slice:
32
+ c.WriteSliceType(t)
33
+ case *types.Array:
34
+ c.WriteArrayType(t)
35
+ case *types.Map:
36
+ c.WriteMapType(t)
37
+ case *types.Chan:
38
+ c.WriteChannelType(t)
39
+ case *types.Interface:
40
+ c.WriteInterfaceType(t, nil) // No ast.InterfaceType available here
41
+ case *types.Signature:
42
+ c.WriteSignatureType(t)
43
+ case *types.Struct:
44
+ c.WriteStructType(t)
45
+ case *types.Alias:
46
+ c.WriteGoType(t.Underlying())
47
+ default:
48
+ // For other types, just write "any" and add a comment
49
+ c.tsw.WriteLiterally("any")
50
+ c.tsw.WriteCommentInlinef("unhandled type: %T", typ)
51
+ }
52
+ }
53
+
54
+ // WriteZeroValueForType writes the TypeScript representation of the zero value
55
+ // for a given Go type.
56
+ // It handles `types.Array` by recursively writing zero values for each element
57
+ // to form a TypeScript array literal (e.g., `[0, 0, 0]`).
58
+ // For `types.Basic` (like `bool`, `string`, numeric types), it writes the
59
+ // corresponding TypeScript zero value (`false`, `""`, `0`).
60
+ // Other types default to `null`. This function is primarily used for initializing
61
+ // arrays and variables where an explicit initializer is absent.
62
+ func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
63
+ switch t := typ.(type) {
64
+ case *types.Array:
65
+ c.tsw.WriteLiterally("[")
66
+ for i := 0; i < int(t.Len()); i++ {
67
+ if i > 0 {
68
+ c.tsw.WriteLiterally(", ")
69
+ }
70
+ c.WriteZeroValueForType(t.Elem())
71
+ }
72
+ c.tsw.WriteLiterally("]")
73
+ case *ast.Expr:
74
+ // For AST expressions, get the type and handle that instead
75
+ if expr := *t; expr != nil {
76
+ if typ := c.pkg.TypesInfo.TypeOf(expr); typ != nil {
77
+ c.WriteZeroValueForType(typ)
78
+ return
79
+ }
80
+ }
81
+ c.tsw.WriteLiterally("null")
82
+ case *types.Basic:
83
+ switch t.Kind() {
84
+ case types.Bool:
85
+ c.tsw.WriteLiterally("false")
86
+ case types.String:
87
+ c.tsw.WriteLiterally(`""`)
88
+ default:
89
+ c.tsw.WriteLiterally("0")
90
+ }
91
+ case *types.Named:
92
+ // Handle named types, especially struct types
93
+ if _, isStruct := t.Underlying().(*types.Struct); isStruct {
94
+ // Initialize struct types with a new instance
95
+ c.tsw.WriteLiterallyf("new %s()", t.Obj().Name())
96
+ return
97
+ }
98
+ // For other named types, use the zero value of the underlying type
99
+ c.WriteZeroValueForType(t.Underlying())
100
+ case *types.Struct:
101
+ // For anonymous struct types, initialize with {}
102
+ c.tsw.WriteLiterally("{}")
103
+ default:
104
+ c.tsw.WriteLiterally("null")
105
+ }
106
+ }
107
+
108
+ // WriteBasicType translates a Go basic type (primitives like int, string, bool)
109
+ // to its TypeScript equivalent.
110
+ // It handles untyped constants by mapping them to appropriate TypeScript types
111
+ // (boolean, number, string, null) and uses GoBuiltinToTypescript for typed primitives.
112
+ func (c *GoToTSCompiler) WriteBasicType(t *types.Basic) {
113
+ name := t.Name()
114
+
115
+ // Handle untyped constants by mapping them to appropriate TypeScript types
116
+ if t.Info()&types.IsUntyped != 0 {
117
+ switch t.Kind() {
118
+ case types.UntypedBool:
119
+ c.tsw.WriteLiterally("boolean")
120
+ return
121
+ case types.UntypedInt, types.UntypedFloat, types.UntypedComplex, types.UntypedRune:
122
+ c.tsw.WriteLiterally("number")
123
+ return
124
+ case types.UntypedString:
125
+ c.tsw.WriteLiterally("string")
126
+ return
127
+ case types.UntypedNil:
128
+ c.tsw.WriteLiterally("null")
129
+ return
130
+ }
131
+ }
132
+
133
+ // For typed basic types, use the existing mapping
134
+ if tsType, ok := GoBuiltinToTypescript(name); ok {
135
+ c.tsw.WriteLiterally(tsType)
136
+ } else {
137
+ c.tsw.WriteLiterally(name)
138
+ }
139
+ }
140
+
141
+ // WriteNamedType translates a Go named type to its TypeScript equivalent.
142
+ // It specially handles the error interface as $.GoError, and uses the original
143
+ // type name for other named types.
144
+ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
145
+ // Check if the named type is the error interface
146
+ if iface, ok := t.Underlying().(*types.Interface); ok && iface.String() == "interface{Error() string}" {
147
+ c.tsw.WriteLiterally("$.GoError")
148
+ } else {
149
+ // Use Obj().Name() for the original defined name
150
+ c.tsw.WriteLiterally(t.Obj().Name())
151
+ }
152
+ }
153
+
154
+ // WritePointerType translates a Go pointer type (*T) to its TypeScript equivalent.
155
+ // It generates $.Box<T_ts> | null, where T_ts is the translated element type.
156
+ func (c *GoToTSCompiler) WritePointerType(t *types.Pointer) {
157
+ c.tsw.WriteLiterally("$.Box<")
158
+ c.WriteGoType(t.Elem())
159
+ c.tsw.WriteLiterally("> | null") // Pointers are always nullable
160
+ }
161
+
162
+ // WriteSliceType translates a Go slice type ([]T) to its TypeScript equivalent.
163
+ // It generates $.Slice<T_ts>, where T_ts is the translated element type.
164
+ func (c *GoToTSCompiler) WriteSliceType(t *types.Slice) {
165
+ c.tsw.WriteLiterally("$.Slice<")
166
+ c.WriteGoType(t.Elem())
167
+ c.tsw.WriteLiterally(">")
168
+ }
169
+
170
+ // WriteArrayType translates a Go array type ([N]T) to its TypeScript equivalent.
171
+ // It generates T_ts[], where T_ts is the translated element type.
172
+ func (c *GoToTSCompiler) WriteArrayType(t *types.Array) {
173
+ c.WriteGoType(t.Elem())
174
+ c.tsw.WriteLiterally("[]") // Arrays cannot be nil
175
+ }
176
+
177
+ // WriteMapType translates a Go map type (map[K]V) to its TypeScript equivalent.
178
+ // It generates Map<K_ts, V_ts>, where K_ts and V_ts are the translated key
179
+ // and element types respectively.
180
+ func (c *GoToTSCompiler) WriteMapType(t *types.Map) {
181
+ c.tsw.WriteLiterally("Map<")
182
+ c.WriteGoType(t.Key())
183
+ c.tsw.WriteLiterally(", ")
184
+ c.WriteGoType(t.Elem())
185
+ c.tsw.WriteLiterally(">")
186
+ }
187
+
188
+ // WriteChannelType translates a Go channel type (chan T) to its TypeScript equivalent.
189
+ // It generates $.Channel<T_ts>, where T_ts is the translated element type.
190
+ func (c *GoToTSCompiler) WriteChannelType(t *types.Chan) {
191
+ c.tsw.WriteLiterally("$.Channel<")
192
+ c.WriteGoType(t.Elem())
193
+ c.tsw.WriteLiterally(">")
194
+ }
195
+
196
+ // WriteFuncType translates a Go function type (`ast.FuncType`) into a TypeScript
197
+ // function signature.
198
+ // The signature is of the form `(param1: type1, param2: type2) => returnType`.
199
+ // - Parameters are written using `WriteFieldList`.
200
+ // - Return types:
201
+ // - If there are no results, the return type is `void`.
202
+ // - If there's a single, unnamed result, it's `resultType`.
203
+ // - If there are multiple or named results, it's a tuple type `[typeA, typeB]`.
204
+ // - If `isAsync` is true (indicating the function is known to perform async
205
+ // operations like channel interactions or contains `go` or `defer` with async calls),
206
+ // the return type is wrapped in `Promise<>` (e.g., `Promise<void>`, `Promise<number>`).
207
+ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
208
+ c.tsw.WriteLiterally("(")
209
+ c.WriteFieldList(exp.Params, true) // true = arguments
210
+ c.tsw.WriteLiterally(")")
211
+ if exp.Results != nil && len(exp.Results.List) > 0 {
212
+ // Use colon for return type annotation
213
+ c.tsw.WriteLiterally(": ")
214
+ if isAsync {
215
+ c.tsw.WriteLiterally("Promise<")
216
+ }
217
+ if len(exp.Results.List) == 1 && len(exp.Results.List[0].Names) == 0 {
218
+ // Single unnamed return type
219
+ typ := c.pkg.TypesInfo.TypeOf(exp.Results.List[0].Type)
220
+ c.WriteGoType(typ)
221
+ } else {
222
+ // Multiple or named return types -> tuple
223
+ c.tsw.WriteLiterally("[")
224
+ for i, field := range exp.Results.List {
225
+ if i > 0 {
226
+ c.tsw.WriteLiterally(", ")
227
+ }
228
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
229
+ c.WriteGoType(typ)
230
+ }
231
+ c.tsw.WriteLiterally("]")
232
+ }
233
+ if isAsync {
234
+ c.tsw.WriteLiterally(">")
235
+ }
236
+ } else {
237
+ // No return value -> void
238
+ if isAsync {
239
+ c.tsw.WriteLiterally(": Promise<void>")
240
+ } else {
241
+ c.tsw.WriteLiterally(": void")
242
+ }
243
+ }
244
+ }
245
+
246
+ // WriteInterfaceType translates a Go interface type to its TypeScript equivalent.
247
+ // It specially handles the error interface as $.GoError, and delegates to
248
+ // writeInterfaceStructure for other interface types, prepending "null | ".
249
+ // If astNode is provided (e.g., from a type spec), comments for methods will be included.
250
+ func (c *GoToTSCompiler) WriteInterfaceType(t *types.Interface, astNode *ast.InterfaceType) {
251
+ // Handle the built-in error interface specifically
252
+ if t.String() == "interface{Error() string}" {
253
+ c.tsw.WriteLiterally("$.GoError")
254
+ return
255
+ }
256
+
257
+ // Prepend "null | " for all other interfaces.
258
+ // writeInterfaceStructure will handle the actual structure like "{...}" or "any".
259
+ c.tsw.WriteLiterally("null | ")
260
+ c.writeInterfaceStructure(t, astNode)
261
+ }
262
+
263
+ // WriteSignatureType translates a Go function signature to its TypeScript equivalent.
264
+ // It generates (param1: type1, param2: type2, ...): returnType for function types.
265
+ func (c *GoToTSCompiler) WriteSignatureType(t *types.Signature) {
266
+ c.tsw.WriteLiterally("(")
267
+ c.tsw.WriteLiterally("(")
268
+ params := t.Params()
269
+ for i := 0; i < params.Len(); i++ {
270
+ if i > 0 {
271
+ c.tsw.WriteLiterally(", ")
272
+ }
273
+
274
+ param := params.At(i)
275
+ paramSlice, paramIsSlice := param.Type().(*types.Slice)
276
+
277
+ paramVariadic := i == params.Len()-1 && t.Variadic()
278
+ if paramVariadic {
279
+ c.tsw.WriteLiterally("...")
280
+ }
281
+
282
+ // Use parameter name if available, otherwise use p0, p1, etc.
283
+ if param.Name() != "" {
284
+ c.tsw.WriteLiterally(param.Name())
285
+ } else {
286
+ c.tsw.WriteLiterallyf("p%d", i)
287
+ }
288
+ c.tsw.WriteLiterally(": ")
289
+
290
+ if paramVariadic && paramIsSlice {
291
+ c.WriteGoType(paramSlice.Elem())
292
+ c.tsw.WriteLiterally("[]")
293
+ } else {
294
+ c.WriteGoType(param.Type())
295
+ }
296
+ }
297
+ c.tsw.WriteLiterally(")")
298
+
299
+ // Handle return types
300
+ c.tsw.WriteLiterally(" => ")
301
+ results := t.Results()
302
+ if results.Len() == 0 {
303
+ c.tsw.WriteLiterally("void")
304
+ } else if results.Len() == 1 {
305
+ c.WriteGoType(results.At(0).Type())
306
+ } else {
307
+ // Multiple return values -> tuple
308
+ c.tsw.WriteLiterally("[")
309
+ for i := 0; i < results.Len(); i++ {
310
+ if i > 0 {
311
+ c.tsw.WriteLiterally(", ")
312
+ }
313
+ c.WriteGoType(results.At(i).Type())
314
+ }
315
+ c.tsw.WriteLiterally("]")
316
+ }
317
+ c.tsw.WriteLiterally(") | null")
318
+ }
319
+
320
+ // writeInterfaceStructure translates a Go `types.Interface` into its TypeScript structural representation.
321
+ // If astNode is provided, it's used to fetch comments for methods.
322
+ // For example, an interface `interface { MethodA(x int) string; EmbeddedB }` might become
323
+ // `{ MethodA(_p0: number): string; } & B_ts`.
324
+ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode *ast.InterfaceType) {
325
+ // Handle empty interface interface{}
326
+ if iface.NumExplicitMethods() == 0 && iface.NumEmbeddeds() == 0 {
327
+ c.tsw.WriteLiterally("any") // Matches current behavior for interface{}
328
+ return
329
+ }
330
+
331
+ // Keep track if we've written any part (methods or first embedded type)
332
+ // to correctly place " & " separators.
333
+ firstPartWritten := false
334
+
335
+ // Handle explicit methods
336
+ if iface.NumExplicitMethods() > 0 {
337
+ c.tsw.WriteLiterally("{") // Opening brace for the object type
338
+ c.tsw.Indent(1)
339
+ c.tsw.WriteLine("") // Newline after opening brace, before the first method
340
+
341
+ for i := 0; i < iface.NumExplicitMethods(); i++ {
342
+ method := iface.ExplicitMethod(i)
343
+ sig := method.Type().(*types.Signature)
344
+
345
+ // Find corresponding ast.Field for comments if astNode is available
346
+ var astField *ast.Field
347
+ if astNode != nil && astNode.Methods != nil {
348
+ for _, f := range astNode.Methods.List {
349
+ // Ensure the field is a named method (not an embedded interface)
350
+ if len(f.Names) > 0 && f.Names[0].Name == method.Name() {
351
+ astField = f
352
+ break
353
+ }
354
+ }
355
+ }
356
+
357
+ // Write comments if astField is found
358
+ if astField != nil {
359
+ if astField.Doc != nil {
360
+ c.WriteDoc(astField.Doc) // WriteDoc handles newlines
361
+ }
362
+ if astField.Comment != nil { // For trailing comments on the same line in Go AST
363
+ c.WriteDoc(astField.Comment)
364
+ }
365
+ }
366
+
367
+ c.tsw.WriteLiterally(method.Name())
368
+ c.tsw.WriteLiterally("(") // Start params
369
+ params := sig.Params()
370
+ for j := 0; j < params.Len(); j++ {
371
+ if j > 0 {
372
+ c.tsw.WriteLiterally(", ")
373
+ }
374
+ paramVar := params.At(j)
375
+ paramName := paramVar.Name()
376
+ if paramName == "" || paramName == "_" {
377
+ paramName = fmt.Sprintf("_p%d", j)
378
+ }
379
+ c.tsw.WriteLiterally(paramName)
380
+ c.tsw.WriteLiterally(": ")
381
+ c.WriteGoType(paramVar.Type()) // Recursive call for param type
382
+ }
383
+ c.tsw.WriteLiterally(")") // End params
384
+
385
+ // Return type
386
+ c.tsw.WriteLiterally(": ")
387
+ results := sig.Results()
388
+ if results.Len() == 0 {
389
+ c.tsw.WriteLiterally("void")
390
+ } else if results.Len() == 1 {
391
+ c.WriteGoType(results.At(0).Type()) // Recursive call for result type
392
+ } else {
393
+ c.tsw.WriteLiterally("[")
394
+ for j := 0; j < results.Len(); j++ {
395
+ if j > 0 {
396
+ c.tsw.WriteLiterally(", ")
397
+ }
398
+ c.WriteGoType(results.At(j).Type()) // Recursive call for result type
399
+ }
400
+ c.tsw.WriteLiterally("]")
401
+ }
402
+ c.tsw.WriteLine("") // newline for each method
403
+ }
404
+ c.tsw.Indent(-1)
405
+ c.tsw.WriteLiterally("}") // Closing brace for the object type
406
+ firstPartWritten = true
407
+ }
408
+
409
+ // Handle embedded types
410
+ if iface.NumEmbeddeds() > 0 {
411
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
412
+ if firstPartWritten {
413
+ c.tsw.WriteLiterally(" & ")
414
+ } else {
415
+ // This is the first part being written (no explicit methods, only embedded)
416
+ firstPartWritten = true
417
+ }
418
+ embeddedType := iface.EmbeddedType(i)
419
+ // When WriteGoType encounters an interface, it will call WriteInterfaceType
420
+ // which will pass nil for astNode, so comments for deeply embedded interface literals
421
+ // might not be available unless they are named types.
422
+ c.WriteGoType(embeddedType)
423
+ }
424
+ }
425
+ }
426
+
427
+ // getTypeString is a utility function that converts a Go `types.Type` into its
428
+ // TypeScript type string representation. It achieves this by creating a temporary
429
+ // `GoToTSCompiler` and `TSCodeWriter` (writing to a `strings.Builder`) and then
430
+ // calling `WriteGoType` on the provided Go type. This allows reusing the main
431
+ // type translation logic to get a string representation of the TypeScript type.
432
+ func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
433
+ var typeStr strings.Builder
434
+ writer := NewTSCodeWriter(&typeStr)
435
+ tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
436
+ tempCompiler.WriteGoType(goType)
437
+ return typeStr.String()
438
+ }
439
+
440
+ // generateFlattenedInitTypeString generates a TypeScript type string for the
441
+ // initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
442
+ // The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
443
+ // including all direct and promoted fields of the `structType`.
444
+ // - It iterates through the direct fields of the `structType`. Exported fields
445
+ // are included with their TypeScript types (obtained via `WriteGoType`).
446
+ // - For anonymous (embedded) fields, it generates a type like `EmbeddedName?: ConstructorParameters<typeof EmbeddedName>[0]`,
447
+ // allowing initialization of the embedded struct's fields directly within the outer struct's initializer.
448
+ // - It then uses `types.NewMethodSet` and checks for `types.Var` objects that are fields
449
+ // to find promoted fields from embedded structs, adding them to the type string if not already present.
450
+ //
451
+ // The resulting string is sorted by field name for deterministic output and represents
452
+ // the shape of the object expected by the struct's TypeScript constructor.
453
+ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named) string {
454
+ if structType == nil {
455
+ return "{}"
456
+ }
457
+
458
+ // Use a map to collect unique field names and their types
459
+ fieldMap := make(map[string]string)
460
+ embeddedTypeMap := make(map[string]string) // Stores TS type string for embedded struct initializers
461
+
462
+ underlying, ok := structType.Underlying().(*types.Struct)
463
+ if !ok {
464
+ return "{}"
465
+ }
466
+
467
+ // First add the direct fields and track embedded types
468
+ for i := 0; i < underlying.NumFields(); i++ {
469
+ field := underlying.Field(i)
470
+ fieldName := field.Name()
471
+
472
+ if !field.Exported() && field.Pkg() != c.pkg.Types {
473
+ continue
474
+ }
475
+
476
+ if field.Anonymous() {
477
+ fieldType := field.Type()
478
+ isPtr := false
479
+ if ptr, ok := fieldType.(*types.Pointer); ok {
480
+ fieldType = ptr.Elem()
481
+ isPtr = true
482
+ }
483
+
484
+ if named, ok := fieldType.(*types.Named); ok {
485
+ embeddedName := named.Obj().Name()
486
+ // For embedded structs, the init type should allow providing fields of the embedded struct,
487
+ // or the embedded struct itself.
488
+ // We generate Partial<EmbeddedStructFields> | EmbeddedStructType
489
+ // This is complex. For now, let's use ConstructorParameters as before for simplicity,
490
+ // or allow the embedded struct type itself.
491
+ // The example `Person?: ConstructorParameters<typeof Person>[0]` is for the fields.
492
+ // Let's try to generate a type that allows either the embedded struct instance or its fields.
493
+ // This might be: `Partial<FlattenedInitType<EmbeddedName>> | EmbeddedName`
494
+ // For now, stick to the simpler `ConstructorParameters<typeof %s>[0]` which implies field-based init.
495
+ // Or, more simply, the type of the embedded struct itself if it's a pointer.
496
+ if isPtr {
497
+ embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type()) // MyEmbeddedType | null
498
+ } else {
499
+ // For value-type embedded structs, allow initializing with its fields.
500
+ // This requires getting the flattened init type for the embedded struct.
501
+ // This could lead to recursion if not handled carefully.
502
+ // A simpler approach for now: use the embedded struct's own type.
503
+ // embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type())
504
+ // Or, using ConstructorParameters to allow field-based initialization:
505
+ embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = fmt.Sprintf("Partial<ConstructorParameters<typeof %s>[0]>", embeddedName)
506
+ }
507
+ }
508
+ continue
509
+ }
510
+ fieldMap[fieldName] = c.getTypeString(field.Type())
511
+ }
512
+
513
+ // Promoted fields (handled by Go's embedding, init should use direct/embedded names)
514
+ // The current logic for `generateFlattenedInitTypeString` seems to focus on top-level
515
+ // settable properties in the constructor. Promoted fields are accessed via `this.promotedField`,
516
+ // not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
517
+
518
+ // Add embedded types to the field map (these are the names of the embedded structs themselves)
519
+ for embeddedName, embeddedTSType := range embeddedTypeMap {
520
+ fieldMap[embeddedName] = embeddedTSType
521
+ }
522
+
523
+ var fieldNames []string
524
+ for name := range fieldMap {
525
+ fieldNames = append(fieldNames, name)
526
+ }
527
+ sort.Strings(fieldNames)
528
+
529
+ var fieldDefs []string
530
+ for _, fieldName := range fieldNames {
531
+ fieldDefs = append(fieldDefs, fmt.Sprintf("%s?: %s", fieldName, fieldMap[fieldName]))
532
+ }
533
+
534
+ return "{" + strings.Join(fieldDefs, ", ") + "}"
535
+ }
536
+
537
+ // WriteStructType translates a Go struct type definition (`ast.StructType`)
538
+ // into a TypeScript anonymous object type (e.g., `{ Field1: Type1; Field2: Type2 }`).
539
+ // If the struct has no fields, it writes `{}`. Otherwise, it delegates to
540
+ // `WriteFieldList` to generate the list of field definitions.
541
+ // Note: This is for anonymous struct type literals. Named struct types are usually
542
+ // handled as classes via `WriteTypeSpec`.
543
+ func (c *GoToTSCompiler) WriteStructType(t *types.Struct) {
544
+ // Generate an interface with the struct's fields
545
+ c.tsw.WriteLiterally("{ ")
546
+ // Add field properties to the interface
547
+ for i := range t.NumFields() {
548
+ field := t.Field(i)
549
+ if i > 0 {
550
+ c.tsw.WriteLiterally("; ")
551
+ }
552
+ c.tsw.WriteLiterally(field.Name() + "?: ")
553
+ c.WriteGoType(field.Type())
554
+ }
555
+ c.tsw.WriteLiterally(" }")
556
+ }
@@ -170,12 +170,27 @@ export interface BaseTypeInfo {
170
170
  kind: TypeKind;
171
171
  zeroValue?: any;
172
172
  }
173
+ /**
174
+ * Represents an argument or a return value of a method.
175
+ */
176
+ export interface MethodArg {
177
+ name?: string;
178
+ type: TypeInfo | string;
179
+ }
180
+ /**
181
+ * Represents the signature of a method, including its name, arguments, and return types.
182
+ */
183
+ export interface MethodSignature {
184
+ name: string;
185
+ args: MethodArg[];
186
+ returns: MethodArg[];
187
+ }
173
188
  /**
174
189
  * Type information for struct types
175
190
  */
176
191
  export interface StructTypeInfo extends BaseTypeInfo {
177
192
  kind: TypeKind.Struct;
178
- methods: Set<string>;
193
+ methods: MethodSignature[];
179
194
  ctor?: new (...args: any[]) => any;
180
195
  fields: Record<string, TypeInfo | string>;
181
196
  }
@@ -184,7 +199,7 @@ export interface StructTypeInfo extends BaseTypeInfo {
184
199
  */
185
200
  export interface InterfaceTypeInfo extends BaseTypeInfo {
186
201
  kind: TypeKind.Interface;
187
- methods: Set<string>;
202
+ methods: MethodSignature[];
188
203
  }
189
204
  /**
190
205
  * Type information for basic types (string, number, boolean)
@@ -229,6 +244,7 @@ export interface FunctionTypeInfo extends BaseTypeInfo {
229
244
  kind: TypeKind.Function;
230
245
  params?: (string | TypeInfo)[];
231
246
  results?: (string | TypeInfo)[];
247
+ isVariadic?: boolean;
232
248
  }
233
249
  /**
234
250
  * Type information for channel types
@@ -256,20 +272,21 @@ export declare function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeIn
256
272
  *
257
273
  * @param name The name of the type.
258
274
  * @param zeroValue The zero value for the type.
259
- * @param methods Set of method names for the struct.
275
+ * @param methods Array of method signatures for the struct.
260
276
  * @param ctor Constructor for the struct.
277
+ * @param fields Record of field names and their types.
261
278
  * @returns The struct type information object.
262
279
  */
263
- export declare const registerStructType: (name: string, zeroValue: any, methods: Set<string>, ctor: new (...args: any[]) => any, fields?: Record<string, TypeInfo | string>) => StructTypeInfo;
280
+ export declare const registerStructType: (name: string, zeroValue: any, methods: MethodSignature[], ctor: new (...args: any[]) => any, fields?: Record<string, TypeInfo | string>) => StructTypeInfo;
264
281
  /**
265
282
  * Registers an interface type with the runtime type system.
266
283
  *
267
284
  * @param name The name of the type.
268
285
  * @param zeroValue The zero value for the type (usually null).
269
- * @param methods Set of method names the interface requires.
286
+ * @param methods Array of method signatures for the interface.
270
287
  * @returns The interface type information object.
271
288
  */
272
- export declare const registerInterfaceType: (name: string, zeroValue: any, methods: Set<string>) => InterfaceTypeInfo;
289
+ export declare const registerInterfaceType: (name: string, zeroValue: any, methods: MethodSignature[]) => InterfaceTypeInfo;
273
290
  /**
274
291
  * Represents the result of a type assertion.
275
292
  */
@@ -277,6 +294,7 @@ export interface TypeAssertResult<T> {
277
294
  value: T;
278
295
  ok: boolean;
279
296
  }
297
+ export declare function areTypeInfosIdentical(type1InfoOrName: string | TypeInfo, type2InfoOrName: string | TypeInfo): boolean;
280
298
  export declare function typeAssert<T>(value: any, typeInfo: string | TypeInfo): TypeAssertResult<T>;
281
299
  /**
282
300
  * Represents the result of a channel receive operation with 'ok' value