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.
package/compiler/expr.go CHANGED
@@ -11,10 +11,35 @@ import (
11
11
 
12
12
  // WriteIndexExpr translates a Go index expression (a[b]) to its TypeScript equivalent.
13
13
  func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
14
+ // Check if this might be a generic function instantiation with a single type argument
15
+ // In this case, the Index should be a type expression, not a value expression
16
+ if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
17
+ // If X is a function type, this might be generic instantiation
18
+ if _, isFuncType := tv.Type.Underlying().(*types.Signature); isFuncType {
19
+ // Check if the index is a type expression (identifier that refers to a type)
20
+ if indexIdent, isIdent := exp.Index.(*ast.Ident); isIdent {
21
+ // Check if this identifier refers to a type
22
+ if obj := c.pkg.TypesInfo.Uses[indexIdent]; obj != nil {
23
+ if _, isTypeName := obj.(*types.TypeName); isTypeName {
24
+ // This is a generic function instantiation: f[T] -> f<T>
25
+ if err := c.WriteValueExpr(exp.X); err != nil {
26
+ return err
27
+ }
28
+ c.tsw.WriteLiterally("<")
29
+ c.WriteTypeExpr(exp.Index)
30
+ c.tsw.WriteLiterally(">")
31
+ return nil
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+
14
38
  // Handle map access: use Map.get() instead of brackets for reading values
15
39
  if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
40
+ underlyingType := tv.Type.Underlying()
16
41
  // Check if it's a map type
17
- if mapType, isMap := tv.Type.Underlying().(*types.Map); isMap {
42
+ if mapType, isMap := underlyingType.(*types.Map); isMap {
18
43
  c.tsw.WriteLiterally("$.mapGet(")
19
44
  if err := c.WriteValueExpr(exp.X); err != nil {
20
45
  return err
@@ -30,6 +55,36 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
30
55
  c.tsw.WriteLiterally(")")
31
56
  return nil
32
57
  }
58
+
59
+ // Check if it's a string type
60
+ if basicType, isBasic := underlyingType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
61
+ c.tsw.WriteLiterally("$.indexString(")
62
+ if err := c.WriteValueExpr(exp.X); err != nil {
63
+ return err
64
+ }
65
+ c.tsw.WriteLiterally(", ")
66
+ if err := c.WriteValueExpr(exp.Index); err != nil {
67
+ return err
68
+ }
69
+ c.tsw.WriteLiterally(")")
70
+ return nil
71
+ }
72
+
73
+ // Check if it's a type parameter with a union constraint (e.g., string | []byte)
74
+ if _, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
75
+ // For type parameters with string | []byte constraint, use specialized function
76
+ // that returns number (byte value) for better TypeScript typing
77
+ c.tsw.WriteLiterally("$.indexStringOrBytes(")
78
+ if err := c.WriteValueExpr(exp.X); err != nil {
79
+ return err
80
+ }
81
+ c.tsw.WriteLiterally(", ")
82
+ if err := c.WriteValueExpr(exp.Index); err != nil {
83
+ return err
84
+ }
85
+ c.tsw.WriteLiterally(")")
86
+ return nil
87
+ }
33
88
  }
34
89
 
35
90
  // Regular array/slice access: use brackets
@@ -44,6 +99,26 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
44
99
  return nil
45
100
  }
46
101
 
102
+ // WriteIndexListExpr translates a Go generic function instantiation (f[T1, T2]) to its TypeScript equivalent (f<T1, T2>).
103
+ func (c *GoToTSCompiler) WriteIndexListExpr(exp *ast.IndexListExpr) error {
104
+ // Write the function expression
105
+ if err := c.WriteValueExpr(exp.X); err != nil {
106
+ return err
107
+ }
108
+
109
+ // Write the type arguments using TypeScript syntax
110
+ c.tsw.WriteLiterally("<")
111
+ for i, typeArg := range exp.Indices {
112
+ if i > 0 {
113
+ c.tsw.WriteLiterally(", ")
114
+ }
115
+ c.WriteTypeExpr(typeArg)
116
+ }
117
+ c.tsw.WriteLiterally(">")
118
+
119
+ return nil
120
+ }
121
+
47
122
  // WriteTypeAssertExpr translates a Go type assertion expression (e.g., `x.(T)`)
48
123
  // into a TypeScript call to `$.typeAssert<T_ts>(x_ts, 'TypeName').value`.
49
124
  // The `$.typeAssert` runtime function handles the actual type check and panic
@@ -52,7 +127,7 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
52
127
  // by the runtime for error messages.
53
128
  func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
54
129
  // Generate a call to $.typeAssert
55
- c.tsw.WriteLiterally("$.typeAssert<")
130
+ c.tsw.WriteLiterally("$.mustTypeAssert<")
56
131
  c.WriteTypeExpr(exp.Type) // Write the asserted type for the generic
57
132
  c.tsw.WriteLiterally(">(")
58
133
  if err := c.WriteValueExpr(exp.X); err != nil { // The interface expression
@@ -60,9 +135,6 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
60
135
  }
61
136
  c.tsw.WriteLiterally(", ")
62
137
 
63
- // Write the type description instead of just the type name
64
- // This ensures we generate proper type info objects for all types
65
-
66
138
  // Unwrap parenthesized expressions to handle cases like r.((<-chan T))
67
139
  typeExpr := exp.Type
68
140
  for {
@@ -75,7 +147,8 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
75
147
 
76
148
  c.writeTypeDescription(typeExpr)
77
149
 
78
- c.tsw.WriteLiterally(")") // Just close the parenthesis, don't access .value directly
150
+ c.tsw.WriteLiterally(")")
151
+
79
152
  return nil
80
153
  }
81
154
 
@@ -137,12 +210,12 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
137
210
  func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
138
211
  // Handle special cases like channel send
139
212
  if exp.Op == token.ARROW {
140
- // Channel send: ch <- val becomes await ch.send(val)
141
- c.tsw.WriteLiterally("await ")
213
+ // Channel send: ch <- val becomes await $.chanSend(ch, val)
214
+ c.tsw.WriteLiterally("await $.chanSend(")
142
215
  if err := c.WriteValueExpr(exp.X); err != nil {
143
216
  return fmt.Errorf("failed to write channel send target: %w", err)
144
217
  }
145
- c.tsw.WriteLiterally(".send(")
218
+ c.tsw.WriteLiterally(", ")
146
219
  if err := c.WriteValueExpr(exp.Y); err != nil {
147
220
  return fmt.Errorf("failed to write channel send value: %w", err)
148
221
  }
@@ -218,6 +291,19 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
218
291
  isBitwise = true
219
292
  }
220
293
 
294
+ // Special handling for large bit shift expressions that would overflow in JavaScript
295
+ if exp.Op == token.SHL {
296
+ // Check if this is 1 << 63 pattern
297
+ if leftLit, leftIsLit := exp.X.(*ast.BasicLit); leftIsLit && leftLit.Value == "1" {
298
+ if rightLit, rightIsLit := exp.Y.(*ast.BasicLit); rightIsLit && rightLit.Value == "63" {
299
+ // Replace 1 << 63 with Number.MAX_SAFE_INTEGER (9007199254740991)
300
+ // This is the largest integer that can be exactly represented in JavaScript
301
+ c.tsw.WriteLiterally("Number.MAX_SAFE_INTEGER")
302
+ return nil
303
+ }
304
+ }
305
+ }
306
+
221
307
  if isBitwise {
222
308
  c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
223
309
  }
@@ -263,12 +349,12 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
263
349
  // their statement contexts (e.g., `IncDecStmt`).
264
350
  func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
265
351
  if exp.Op == token.ARROW {
266
- // Channel receive: <-ch becomes await ch.receive()
267
- c.tsw.WriteLiterally("await ")
352
+ // Channel receive: <-ch becomes await $.chanRecv(ch)
353
+ c.tsw.WriteLiterally("await $.chanRecv(")
268
354
  if err := c.WriteValueExpr(exp.X); err != nil {
269
355
  return fmt.Errorf("failed to write channel receive operand: %w", err)
270
356
  }
271
- c.tsw.WriteLiterally(".receive()")
357
+ c.tsw.WriteLiterally(")")
272
358
  return nil
273
359
  }
274
360
 
@@ -337,6 +423,155 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
337
423
  return nil
338
424
  }
339
425
 
426
+ // WriteSliceExpr translates a Go slice expression (e.g., `s[low:high:max]`) to its TypeScript equivalent.
427
+ // If `s` is a string and it's not a 3-index slice, it uses `s.substring(low, high)`.
428
+ // If `s` is `[]byte` (Uint8Array) and it's not a 3-index slice, it uses `s.subarray(low, high)`.
429
+ // Otherwise, it falls back to the `$.goSlice(s, low, high, max)` runtime helper.
430
+ func (c *GoToTSCompiler) WriteSliceExpr(exp *ast.SliceExpr) error {
431
+ // Check if the expression being sliced is a string
432
+ tv := c.pkg.TypesInfo.TypeOf(exp.X)
433
+ isString := false
434
+ isByteSlice := false
435
+ isTypeParam := false
436
+ if tv != nil {
437
+ if basicType, isBasic := tv.Underlying().(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
438
+ isString = true
439
+ }
440
+ if sliceType, isSlice := tv.Underlying().(*types.Slice); isSlice {
441
+ if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
442
+ isByteSlice = true
443
+ }
444
+ }
445
+ if _, isTP := tv.(*types.TypeParam); isTP {
446
+ isTypeParam = true
447
+ }
448
+ }
449
+
450
+ // Handle type parameters with union constraints (e.g., string | []byte)
451
+ if isTypeParam {
452
+ // For type parameters, we need to create a runtime helper that handles both string and []byte
453
+ c.tsw.WriteLiterally("$.sliceStringOrBytes(")
454
+ if err := c.WriteValueExpr(exp.X); err != nil {
455
+ return err
456
+ }
457
+ c.tsw.WriteLiterally(", ")
458
+ if exp.Low != nil {
459
+ if err := c.WriteValueExpr(exp.Low); err != nil {
460
+ return err
461
+ }
462
+ } else {
463
+ c.tsw.WriteLiterally("undefined")
464
+ }
465
+ c.tsw.WriteLiterally(", ")
466
+ if exp.High != nil {
467
+ if err := c.WriteValueExpr(exp.High); err != nil {
468
+ return err
469
+ }
470
+ } else {
471
+ c.tsw.WriteLiterally("undefined")
472
+ }
473
+ if exp.Slice3 {
474
+ c.tsw.WriteLiterally(", ")
475
+ if exp.Max != nil {
476
+ if err := c.WriteValueExpr(exp.Max); err != nil {
477
+ return err
478
+ }
479
+ } else {
480
+ c.tsw.WriteLiterally("undefined")
481
+ }
482
+ }
483
+ c.tsw.WriteLiterally(")")
484
+ return nil
485
+ }
486
+
487
+ if isString && !exp.Slice3 {
488
+ // Use $.sliceString for byte-correct string slicing
489
+ c.tsw.WriteLiterally("$.sliceString(")
490
+ if err := c.WriteValueExpr(exp.X); err != nil {
491
+ return err
492
+ }
493
+ c.tsw.WriteLiterally(", ")
494
+ if exp.Low != nil {
495
+ if err := c.WriteValueExpr(exp.Low); err != nil {
496
+ return err
497
+ }
498
+ } else {
499
+ // Go's default low for string[:high] is 0.
500
+ // $.sliceString can handle undefined for low as 0.
501
+ c.tsw.WriteLiterally("undefined")
502
+ }
503
+ c.tsw.WriteLiterally(", ")
504
+ if exp.High != nil {
505
+ if err := c.WriteValueExpr(exp.High); err != nil {
506
+ return err
507
+ }
508
+ } else {
509
+ // Go's default high for string[low:] means to the end.
510
+ // $.sliceString can handle undefined for high as end of string.
511
+ c.tsw.WriteLiterally("undefined")
512
+ }
513
+ c.tsw.WriteLiterally(")")
514
+ } else if isByteSlice && !exp.Slice3 {
515
+ // Use s.subarray(low, high) for []byte slices
516
+ if err := c.WriteValueExpr(exp.X); err != nil {
517
+ return err
518
+ }
519
+ c.tsw.WriteLiterally(".subarray(")
520
+ if exp.Low != nil {
521
+ if err := c.WriteValueExpr(exp.Low); err != nil {
522
+ return err
523
+ }
524
+ } else {
525
+ c.tsw.WriteLiterally("0") // Default low for subarray is 0
526
+ }
527
+ if exp.High != nil {
528
+ c.tsw.WriteLiterally(", ")
529
+ if err := c.WriteValueExpr(exp.High); err != nil {
530
+ return err
531
+ }
532
+ } else {
533
+ // If high is omitted, subarray goes to the end of the array.
534
+ // No need to write undefined or length, just close the parenthesis if low was the last arg.
535
+ }
536
+ c.tsw.WriteLiterally(")")
537
+ } else {
538
+ // Fallback to $.goSlice for actual slices (arrays) or 3-index string slices (which are rare and might need $.goSlice's complexity)
539
+ // Or if it's a string but has Slice3, it's not handled by simple substring.
540
+ c.tsw.WriteLiterally("$.goSlice(")
541
+ if err := c.WriteValueExpr(exp.X); err != nil {
542
+ return err
543
+ }
544
+ c.tsw.WriteLiterally(", ")
545
+ if exp.Low != nil {
546
+ if err := c.WriteValueExpr(exp.Low); err != nil {
547
+ return err
548
+ }
549
+ } else {
550
+ c.tsw.WriteLiterally("undefined")
551
+ }
552
+ c.tsw.WriteLiterally(", ")
553
+ if exp.High != nil {
554
+ if err := c.WriteValueExpr(exp.High); err != nil {
555
+ return err
556
+ }
557
+ } else {
558
+ c.tsw.WriteLiterally("undefined")
559
+ }
560
+ if exp.Slice3 {
561
+ c.tsw.WriteLiterally(", ")
562
+ if exp.Max != nil {
563
+ if err := c.WriteValueExpr(exp.Max); err != nil {
564
+ return err
565
+ }
566
+ } else {
567
+ c.tsw.WriteLiterally("undefined")
568
+ }
569
+ }
570
+ c.tsw.WriteLiterally(")")
571
+ }
572
+ return nil
573
+ }
574
+
340
575
  // WriteKeyValueExpr translates a Go key-value pair expression (`ast.KeyValueExpr`),
341
576
  // typically found within composite literals (for structs, maps, or arrays with
342
577
  // indexed elements), into its TypeScript object property equivalent: `key: value`.
package/compiler/field.go CHANGED
@@ -50,7 +50,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
50
50
  c.tsw.WriteLiterally(name.Name)
51
51
  c.tsw.WriteLiterally(": ")
52
52
  typ := c.pkg.TypesInfo.TypeOf(field.Type)
53
- c.WriteGoType(typ)
53
+ c.WriteGoType(typ, GoTypeContextGeneral)
54
54
  }
55
55
  }
56
56
 
@@ -89,7 +89,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
89
89
  c.tsw.WriteLiterally(name.Name)
90
90
  c.tsw.WriteLiterally(": ")
91
91
  typ := c.pkg.TypesInfo.TypeOf(field.Type)
92
- c.WriteGoType(typ) // Use WriteGoType for parameter type
92
+ c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for parameter type
93
93
  }
94
94
  } else {
95
95
  // For struct fields and other non-argument fields
@@ -155,7 +155,7 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
155
155
  // write type for struct fields (not arguments)
156
156
  c.tsw.WriteLiterally(": ")
157
157
  typ := c.pkg.TypesInfo.TypeOf(field.Type)
158
- c.WriteGoType(typ) // Use WriteGoType for field type
158
+ c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for field type
159
159
 
160
160
  if !isArguments {
161
161
  // write tag comment if any for struct fields
package/compiler/lit.go CHANGED
@@ -1,6 +1,7 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
+ "fmt"
4
5
  "go/ast"
5
6
  "go/token"
6
7
  "strconv"
@@ -90,9 +91,40 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
90
91
 
91
92
  c.tsw.WriteLiterally(" => ")
92
93
 
94
+ hasNamedReturns := false
95
+ if exp.Type.Results != nil {
96
+ for _, field := range exp.Type.Results.List {
97
+ if len(field.Names) > 0 {
98
+ hasNamedReturns = true
99
+ break
100
+ }
101
+ }
102
+ }
103
+
104
+ if hasNamedReturns {
105
+ c.tsw.WriteLine("{")
106
+ c.tsw.Indent(1)
107
+
108
+ // Declare named return variables and initialize them to their zero values
109
+ for _, field := range exp.Type.Results.List {
110
+ for _, name := range field.Names {
111
+ c.tsw.WriteLiterallyf("let %s: ", name.Name)
112
+ c.WriteTypeExpr(field.Type)
113
+ c.tsw.WriteLiterally(" = ")
114
+ c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
115
+ c.tsw.WriteLine("")
116
+ }
117
+ }
118
+ }
119
+
93
120
  // Write function body
94
- if err := c.WriteStmt(exp.Body); err != nil {
95
- return err
121
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
122
+ return fmt.Errorf("failed to write block statement: %w", err)
123
+ }
124
+
125
+ if hasNamedReturns {
126
+ c.tsw.Indent(-1)
127
+ c.tsw.WriteLiterally("}")
96
128
  }
97
129
 
98
130
  return nil
@@ -29,7 +29,10 @@ var goToTypescriptPrimitives = map[string]string{
29
29
  "int16": "number",
30
30
  "int32": "number",
31
31
  "rune": "number", // alias for int32
32
- "int64": "bigint", // Requires TypeScript target >= ES2020
32
+
33
+ // TODO: add bigint support
34
+ // "int64": "bigint", // Requires TypeScript target >= ES2020
35
+ "int64": "number",
33
36
 
34
37
  // Unsigned Integers
35
38
  "uint": "number",
@@ -37,7 +40,10 @@ var goToTypescriptPrimitives = map[string]string{
37
40
  "byte": "number",
38
41
  "uint16": "number",
39
42
  "uint32": "number",
40
- "uint64": "bigint", // Requires TypeScript target >= ES2020
43
+
44
+ // TODO: add bigint support
45
+ // "uint64": "bigint", // Requires TypeScript target >= ES2020
46
+ "uint64": "number",
41
47
 
42
48
  // Floating Point Numbers
43
49
  "float32": "number",
@@ -4,6 +4,7 @@ import (
4
4
  "fmt"
5
5
  "go/ast"
6
6
  "go/types"
7
+ "sort"
7
8
  "strings"
8
9
  )
9
10
 
@@ -23,6 +24,12 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
23
24
  if err := c.WriteValueExpr(a.Name); err != nil {
24
25
  return err
25
26
  }
27
+
28
+ // Write type parameters if present (for generics)
29
+ if a.TypeParams != nil {
30
+ c.WriteTypeParameters(a.TypeParams)
31
+ }
32
+
26
33
  c.tsw.WriteLiterally(" ")
27
34
  c.tsw.WriteLine("{")
28
35
  c.tsw.Indent(1)
@@ -121,9 +128,25 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
121
128
  c.tsw.WriteLine("")
122
129
 
123
130
  // Generate the clone method
124
- c.tsw.WriteLinef("public clone(): %s {", className)
131
+ cloneReturnType := className
132
+ if a.TypeParams != nil && len(a.TypeParams.List) > 0 {
133
+ cloneReturnType += "<"
134
+ first := true
135
+ for _, field := range a.TypeParams.List {
136
+ for _, name := range field.Names {
137
+ if !first {
138
+ cloneReturnType += ", "
139
+ }
140
+ first = false
141
+ cloneReturnType += name.Name
142
+ }
143
+ }
144
+ cloneReturnType += ">"
145
+ }
146
+
147
+ c.tsw.WriteLinef("public clone(): %s {", cloneReturnType)
125
148
  c.tsw.Indent(1)
126
- c.tsw.WriteLinef("const cloned = new %s()", className)
149
+ c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType)
127
150
  c.tsw.WriteLine("cloned._fields = {")
128
151
  c.tsw.Indent(1)
129
152
 
@@ -164,7 +187,18 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
164
187
  if starExpr, ok := recvType.(*ast.StarExpr); ok {
165
188
  recvType = starExpr.X
166
189
  }
167
- if ident, ok := recvType.(*ast.Ident); ok && ident.Name == className {
190
+
191
+ // Check for both simple identifiers (Pair) and generic types (Pair[T])
192
+ var recvTypeName string
193
+ if ident, ok := recvType.(*ast.Ident); ok {
194
+ recvTypeName = ident.Name
195
+ } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
196
+ if ident, ok := indexExpr.X.(*ast.Ident); ok {
197
+ recvTypeName = ident.Name
198
+ }
199
+ }
200
+
201
+ if recvTypeName == className {
168
202
  c.tsw.WriteLine("")
169
203
  if err := c.WriteFuncDeclAsMethod(funcDecl); err != nil {
170
204
  return err
@@ -292,21 +326,21 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
292
326
  }
293
327
  c.tsw.WriteLiterally(paramName)
294
328
  c.tsw.WriteLiterally(": ")
295
- c.WriteGoType(param.Type())
329
+ c.WriteGoType(param.Type(), GoTypeContextGeneral)
296
330
  }
297
331
  c.tsw.WriteLiterally(")")
298
332
  results := sig.Results()
299
333
  if results.Len() > 0 {
300
334
  c.tsw.WriteLiterally(": ")
301
335
  if results.Len() == 1 {
302
- c.WriteGoType(results.At(0).Type())
336
+ c.WriteGoType(results.At(0).Type(), GoTypeContextFunctionReturn)
303
337
  } else {
304
338
  c.tsw.WriteLiterally("[")
305
339
  for j := 0; j < results.Len(); j++ {
306
340
  if j > 0 {
307
341
  c.tsw.WriteLiterally(", ")
308
342
  }
309
- c.WriteGoType(results.At(j).Type())
343
+ c.WriteGoType(results.At(j).Type(), GoTypeContextFunctionReturn)
310
344
  }
311
345
  c.tsw.WriteLiterally("]")
312
346
  }
@@ -380,3 +414,100 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
380
414
  c.tsw.WriteLine("}")
381
415
  return nil
382
416
  }
417
+
418
+ // generateFlattenedInitTypeString generates a TypeScript type string for the
419
+ // initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
420
+ // The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
421
+ // including all direct and promoted fields of the `structType`.
422
+ // - It iterates through the direct fields of the `structType`. Exported fields
423
+ // are included with their TypeScript types (obtained via `WriteGoType`).
424
+ // - For anonymous (embedded) fields, it generates a type like `EmbeddedName?: ConstructorParameters<typeof EmbeddedName>[0]`,
425
+ // allowing initialization of the embedded struct's fields directly within the outer struct's initializer.
426
+ // - It then uses `types.NewMethodSet` and checks for `types.Var` objects that are fields
427
+ // to find promoted fields from embedded structs, adding them to the type string if not already present.
428
+ //
429
+ // The resulting string is sorted by field name for deterministic output and represents
430
+ // the shape of the object expected by the struct's TypeScript constructor.
431
+ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named) string {
432
+ if structType == nil {
433
+ return "{}"
434
+ }
435
+
436
+ // Use a map to collect unique field names and their types
437
+ fieldMap := make(map[string]string)
438
+ embeddedTypeMap := make(map[string]string) // Stores TS type string for embedded struct initializers
439
+
440
+ underlying, ok := structType.Underlying().(*types.Struct)
441
+ if !ok {
442
+ return "{}"
443
+ }
444
+
445
+ // First add the direct fields and track embedded types
446
+ for i := 0; i < underlying.NumFields(); i++ {
447
+ field := underlying.Field(i)
448
+ fieldName := field.Name()
449
+
450
+ if !field.Exported() && field.Pkg() != c.pkg.Types {
451
+ continue
452
+ }
453
+
454
+ if field.Anonymous() {
455
+ fieldType := field.Type()
456
+ isPtr := false
457
+ if ptr, ok := fieldType.(*types.Pointer); ok {
458
+ fieldType = ptr.Elem()
459
+ isPtr = true
460
+ }
461
+
462
+ if named, ok := fieldType.(*types.Named); ok {
463
+ embeddedName := named.Obj().Name()
464
+ // For embedded structs, the init type should allow providing fields of the embedded struct,
465
+ // or the embedded struct itself.
466
+ // We generate Partial<EmbeddedStructFields> | EmbeddedStructType
467
+ // This is complex. For now, let's use ConstructorParameters as before for simplicity,
468
+ // or allow the embedded struct type itself.
469
+ // The example `Person?: ConstructorParameters<typeof Person>[0]` is for the fields.
470
+ // Let's try to generate a type that allows either the embedded struct instance or its fields.
471
+ // This might be: `Partial<FlattenedInitType<EmbeddedName>> | EmbeddedName`
472
+ // For now, stick to the simpler `ConstructorParameters<typeof %s>[0]` which implies field-based init.
473
+ // Or, more simply, the type of the embedded struct itself if it's a pointer.
474
+ if isPtr {
475
+ embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type()) // MyEmbeddedType | null
476
+ } else {
477
+ // For value-type embedded structs, allow initializing with its fields.
478
+ // This requires getting the flattened init type for the embedded struct.
479
+ // This could lead to recursion if not handled carefully.
480
+ // A simpler approach for now: use the embedded struct's own type.
481
+ // embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type())
482
+ // Or, using ConstructorParameters to allow field-based initialization:
483
+ embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = fmt.Sprintf("Partial<ConstructorParameters<typeof %s>[0]>", embeddedName)
484
+ }
485
+ }
486
+ continue
487
+ }
488
+ fieldMap[fieldName] = c.getTypeString(field.Type())
489
+ }
490
+
491
+ // Promoted fields (handled by Go's embedding, init should use direct/embedded names)
492
+ // The current logic for `generateFlattenedInitTypeString` seems to focus on top-level
493
+ // settable properties in the constructor. Promoted fields are accessed via `this.promotedField`,
494
+ // not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
495
+
496
+ // Add embedded types to the field map (these are the names of the embedded structs themselves)
497
+ for embeddedName, embeddedTSType := range embeddedTypeMap {
498
+ fieldMap[embeddedName] = embeddedTSType
499
+ }
500
+
501
+ var fieldNames []string
502
+ for name := range fieldMap {
503
+ fieldNames = append(fieldNames, name)
504
+ }
505
+ sort.Strings(fieldNames)
506
+
507
+ var fieldDefs []string
508
+ for _, fieldName := range fieldNames {
509
+ fieldDefs = append(fieldDefs, fmt.Sprintf("%s?: %s", fieldName, fieldMap[fieldName]))
510
+ }
511
+
512
+ return "{" + strings.Join(fieldDefs, ", ") + "}"
513
+ }