goscript 0.0.22 → 0.0.24

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.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
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
 
@@ -84,7 +157,7 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
84
157
  // of the left (X) and right (Y) operands of the binary expression.
85
158
  // Returns `true` if both operands are determined to be pointer types,
86
159
  // `false` otherwise. This is used to apply specific comparison semantics
87
- // for pointers (e.g., comparing the box objects directly).
160
+ // for pointers (e.g., comparing the varRef objects directly).
88
161
  func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
89
162
  leftType := c.pkg.TypesInfo.TypeOf(exp.X)
90
163
  rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
@@ -122,10 +195,10 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
122
195
  // It handles several cases:
123
196
  // - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
124
197
  // - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
125
- // pointer (which may be a box object or `null`) directly to `null` using
198
+ // pointer (which may be a varRef object or `null`) directly to `null` using
126
199
  // the translated operator (`==` or `!=`).
127
200
  // - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
128
- // the box objects directly using strict equality (`===` or `!==`).
201
+ // the varRef objects directly using strict equality (`===` or `!==`).
129
202
  // - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
130
203
  // in parentheses `()` to ensure correct precedence in TypeScript, and operators
131
204
  // are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
@@ -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
  }
@@ -170,9 +243,26 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
170
243
  }
171
244
 
172
245
  if isNilComparison {
173
- // Compare the box object directly to null
174
- if err := c.WriteValueExpr(ptrExpr); err != nil {
175
- return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
246
+ // For nil comparisons, we need to decide whether to write .value or not
247
+ // If the pointer variable is varrefed, we need to access .value
248
+ if ident, ok := ptrExpr.(*ast.Ident); ok {
249
+ if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
250
+ if c.analysis.NeedsVarRef(obj) {
251
+ // Variable is varrefed, so we need to access .value
252
+ c.WriteIdent(ident, true) // This will add .value
253
+ } else {
254
+ // Variable is not varrefed, write directly
255
+ c.WriteIdent(ident, false)
256
+ }
257
+ } else {
258
+ // No object info, write directly
259
+ c.WriteIdent(ident, false)
260
+ }
261
+ } else {
262
+ // For other expressions, use WriteValueExpr (but this might need review)
263
+ if err := c.WriteValueExpr(ptrExpr); err != nil {
264
+ return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
265
+ }
176
266
  }
177
267
  c.tsw.WriteLiterally(" ")
178
268
  tokStr, ok := TokenToTs(exp.Op)
@@ -185,7 +275,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
185
275
  }
186
276
 
187
277
  // Check if this is a pointer comparison (non-nil)
188
- // Compare the box objects directly using === or !==
278
+ // Compare the varRef objects directly using === or !==
189
279
  if c.isPointerComparison(exp) {
190
280
  c.tsw.WriteLiterally("(") // Wrap comparison
191
281
  if err := c.WriteValueExpr(exp.X); err != nil {
@@ -211,6 +301,50 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
211
301
  return nil
212
302
  }
213
303
 
304
+ // Check for Duration arithmetic operations (multiplication)
305
+ if exp.Op == token.MUL && c.pkg != nil && c.pkg.TypesInfo != nil {
306
+ leftType := c.pkg.TypesInfo.TypeOf(exp.X)
307
+ rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
308
+
309
+ // Check if left operand is a Duration type (from time package)
310
+ if leftType != nil {
311
+ if namedType, ok := leftType.(*types.Named); ok {
312
+ if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
313
+ // Duration * number -> Duration.multiply(duration, number)
314
+ c.tsw.WriteLiterally("$.multiplyDuration(")
315
+ if err := c.WriteValueExpr(exp.X); err != nil {
316
+ return fmt.Errorf("failed to write Duration in multiplication: %w", err)
317
+ }
318
+ c.tsw.WriteLiterally(", ")
319
+ if err := c.WriteValueExpr(exp.Y); err != nil {
320
+ return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
321
+ }
322
+ c.tsw.WriteLiterally(")")
323
+ return nil
324
+ }
325
+ }
326
+ }
327
+
328
+ // Check if right operand is a Duration type (number * Duration)
329
+ if rightType != nil {
330
+ if namedType, ok := rightType.(*types.Named); ok {
331
+ if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
332
+ // number * Duration -> Duration.multiply(duration, number)
333
+ c.tsw.WriteLiterally("$.multiplyDuration(")
334
+ if err := c.WriteValueExpr(exp.Y); err != nil {
335
+ return fmt.Errorf("failed to write Duration in multiplication: %w", err)
336
+ }
337
+ c.tsw.WriteLiterally(", ")
338
+ if err := c.WriteValueExpr(exp.X); err != nil {
339
+ return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
340
+ }
341
+ c.tsw.WriteLiterally(")")
342
+ return nil
343
+ }
344
+ }
345
+ }
346
+ }
347
+
214
348
  // Check if the operator is a bitwise operator
215
349
  isBitwise := false
216
350
  switch exp.Op {
@@ -218,6 +352,19 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
218
352
  isBitwise = true
219
353
  }
220
354
 
355
+ // Special handling for large bit shift expressions that would overflow in JavaScript
356
+ if exp.Op == token.SHL {
357
+ // Check if this is 1 << 63 pattern
358
+ if leftLit, leftIsLit := exp.X.(*ast.BasicLit); leftIsLit && leftLit.Value == "1" {
359
+ if rightLit, rightIsLit := exp.Y.(*ast.BasicLit); rightIsLit && rightLit.Value == "63" {
360
+ // Replace 1 << 63 with Number.MAX_SAFE_INTEGER (9007199254740991)
361
+ // This is the largest integer that can be exactly represented in JavaScript
362
+ c.tsw.WriteLiterally("Number.MAX_SAFE_INTEGER")
363
+ return nil
364
+ }
365
+ }
366
+ }
367
+
221
368
  if isBitwise {
222
369
  c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
223
370
  }
@@ -248,11 +395,11 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
248
395
  // It handles several unary operations:
249
396
  // - Channel receive (`<-ch`): Becomes `await ch.receive()`.
250
397
  // - Address-of (`&var`):
251
- // - If `var` is a boxed variable (its address was taken), `&var` evaluates
252
- // to the box itself (i.e., `varName` in TypeScript, which holds the box).
253
- // - Otherwise (e.g., `&unboxedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
398
+ // - If `var` is a varrefed variable (its address was taken), `&var` evaluates
399
+ // to the varRef itself (i.e., `varName` in TypeScript, which holds the varRef).
400
+ // - Otherwise (e.g., `&unvarrefedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
254
401
  // the operand `var`. The resulting TypeScript value (e.g., a new object instance)
255
- // acts as the "pointer". Boxing decisions for such pointers are handled at
402
+ // acts as the "pointer". VarRefing decisions for such pointers are handled at
256
403
  // the assignment site.
257
404
  // - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
258
405
  // equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
@@ -263,36 +410,36 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
263
410
  // their statement contexts (e.g., `IncDecStmt`).
264
411
  func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
265
412
  if exp.Op == token.ARROW {
266
- // Channel receive: <-ch becomes await ch.receive()
267
- c.tsw.WriteLiterally("await ")
413
+ // Channel receive: <-ch becomes await $.chanRecv(ch)
414
+ c.tsw.WriteLiterally("await $.chanRecv(")
268
415
  if err := c.WriteValueExpr(exp.X); err != nil {
269
416
  return fmt.Errorf("failed to write channel receive operand: %w", err)
270
417
  }
271
- c.tsw.WriteLiterally(".receive()")
418
+ c.tsw.WriteLiterally(")")
272
419
  return nil
273
420
  }
274
421
 
275
422
  if exp.Op == token.AND { // Address-of operator (&)
276
- // If the operand is an identifier for a variable that is boxed,
277
- // the result of & is the box itself.
423
+ // If the operand is an identifier for a variable that is varrefed,
424
+ // the result of & is the varRef itself.
278
425
  if ident, ok := exp.X.(*ast.Ident); ok {
279
426
  var obj types.Object
280
427
  obj = c.pkg.TypesInfo.Uses[ident]
281
428
  if obj == nil {
282
429
  obj = c.pkg.TypesInfo.Defs[ident]
283
430
  }
284
- if obj != nil && c.analysis.NeedsBoxed(obj) {
285
- // &boxedVar -> boxedVar (the box itself)
286
- c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the box)
431
+ if obj != nil && c.analysis.NeedsVarRef(obj) {
432
+ // &varRefVar -> varRefVar (the variable reference itself)
433
+ c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the variable reference)
287
434
  return nil
288
435
  }
289
436
  }
290
437
 
291
- // Otherwise (&unboxedVar, &CompositeLit{}, &FuncCall(), etc.),
438
+ // Otherwise (&unvarrefedVar, &CompositeLit{}, &FuncCall(), etc.),
292
439
  // the address-of operator in Go, when used to create a pointer,
293
440
  // translates to simply evaluating the operand in TypeScript.
294
441
  // The resulting value (e.g., a new object instance) acts as the "pointer".
295
- // Boxing decisions are handled at the assignment site based on the LHS variable.
442
+ // VarRefing decisions are handled at the assignment site based on the LHS variable.
296
443
  if err := c.WriteValueExpr(exp.X); err != nil {
297
444
  return fmt.Errorf("failed to write &-operand: %w", err)
298
445
  }
@@ -337,6 +484,155 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
337
484
  return nil
338
485
  }
339
486
 
487
+ // WriteSliceExpr translates a Go slice expression (e.g., `s[low:high:max]`) to its TypeScript equivalent.
488
+ // If `s` is a string and it's not a 3-index slice, it uses `s.substring(low, high)`.
489
+ // If `s` is `[]byte` (Uint8Array) and it's not a 3-index slice, it uses `s.subarray(low, high)`.
490
+ // Otherwise, it falls back to the `$.goSlice(s, low, high, max)` runtime helper.
491
+ func (c *GoToTSCompiler) WriteSliceExpr(exp *ast.SliceExpr) error {
492
+ // Check if the expression being sliced is a string
493
+ tv := c.pkg.TypesInfo.TypeOf(exp.X)
494
+ isString := false
495
+ isByteSlice := false
496
+ isTypeParam := false
497
+ if tv != nil {
498
+ if basicType, isBasic := tv.Underlying().(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
499
+ isString = true
500
+ }
501
+ if sliceType, isSlice := tv.Underlying().(*types.Slice); isSlice {
502
+ if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
503
+ isByteSlice = true
504
+ }
505
+ }
506
+ if _, isTP := tv.(*types.TypeParam); isTP {
507
+ isTypeParam = true
508
+ }
509
+ }
510
+
511
+ // Handle type parameters with union constraints (e.g., string | []byte)
512
+ if isTypeParam {
513
+ // For type parameters, we need to create a runtime helper that handles both string and []byte
514
+ c.tsw.WriteLiterally("$.sliceStringOrBytes(")
515
+ if err := c.WriteValueExpr(exp.X); err != nil {
516
+ return err
517
+ }
518
+ c.tsw.WriteLiterally(", ")
519
+ if exp.Low != nil {
520
+ if err := c.WriteValueExpr(exp.Low); err != nil {
521
+ return err
522
+ }
523
+ } else {
524
+ c.tsw.WriteLiterally("undefined")
525
+ }
526
+ c.tsw.WriteLiterally(", ")
527
+ if exp.High != nil {
528
+ if err := c.WriteValueExpr(exp.High); err != nil {
529
+ return err
530
+ }
531
+ } else {
532
+ c.tsw.WriteLiterally("undefined")
533
+ }
534
+ if exp.Slice3 {
535
+ c.tsw.WriteLiterally(", ")
536
+ if exp.Max != nil {
537
+ if err := c.WriteValueExpr(exp.Max); err != nil {
538
+ return err
539
+ }
540
+ } else {
541
+ c.tsw.WriteLiterally("undefined")
542
+ }
543
+ }
544
+ c.tsw.WriteLiterally(")")
545
+ return nil
546
+ }
547
+
548
+ if isString && !exp.Slice3 {
549
+ // Use $.sliceString for byte-correct string slicing
550
+ c.tsw.WriteLiterally("$.sliceString(")
551
+ if err := c.WriteValueExpr(exp.X); err != nil {
552
+ return err
553
+ }
554
+ c.tsw.WriteLiterally(", ")
555
+ if exp.Low != nil {
556
+ if err := c.WriteValueExpr(exp.Low); err != nil {
557
+ return err
558
+ }
559
+ } else {
560
+ // Go's default low for string[:high] is 0.
561
+ // $.sliceString can handle undefined for low as 0.
562
+ c.tsw.WriteLiterally("undefined")
563
+ }
564
+ c.tsw.WriteLiterally(", ")
565
+ if exp.High != nil {
566
+ if err := c.WriteValueExpr(exp.High); err != nil {
567
+ return err
568
+ }
569
+ } else {
570
+ // Go's default high for string[low:] means to the end.
571
+ // $.sliceString can handle undefined for high as end of string.
572
+ c.tsw.WriteLiterally("undefined")
573
+ }
574
+ c.tsw.WriteLiterally(")")
575
+ } else if isByteSlice && !exp.Slice3 {
576
+ // Use s.subarray(low, high) for []byte slices
577
+ if err := c.WriteValueExpr(exp.X); err != nil {
578
+ return err
579
+ }
580
+ c.tsw.WriteLiterally(".subarray(")
581
+ if exp.Low != nil {
582
+ if err := c.WriteValueExpr(exp.Low); err != nil {
583
+ return err
584
+ }
585
+ } else {
586
+ c.tsw.WriteLiterally("0") // Default low for subarray is 0
587
+ }
588
+ if exp.High != nil {
589
+ c.tsw.WriteLiterally(", ")
590
+ if err := c.WriteValueExpr(exp.High); err != nil {
591
+ return err
592
+ }
593
+ } else {
594
+ // If high is omitted, subarray goes to the end of the array.
595
+ // No need to write undefined or length, just close the parenthesis if low was the last arg.
596
+ }
597
+ c.tsw.WriteLiterally(")")
598
+ } else {
599
+ // Fallback to $.goSlice for actual slices (arrays) or 3-index string slices (which are rare and might need $.goSlice's complexity)
600
+ // Or if it's a string but has Slice3, it's not handled by simple substring.
601
+ c.tsw.WriteLiterally("$.goSlice(")
602
+ if err := c.WriteValueExpr(exp.X); err != nil {
603
+ return err
604
+ }
605
+ c.tsw.WriteLiterally(", ")
606
+ if exp.Low != nil {
607
+ if err := c.WriteValueExpr(exp.Low); err != nil {
608
+ return err
609
+ }
610
+ } else {
611
+ c.tsw.WriteLiterally("undefined")
612
+ }
613
+ c.tsw.WriteLiterally(", ")
614
+ if exp.High != nil {
615
+ if err := c.WriteValueExpr(exp.High); err != nil {
616
+ return err
617
+ }
618
+ } else {
619
+ c.tsw.WriteLiterally("undefined")
620
+ }
621
+ if exp.Slice3 {
622
+ c.tsw.WriteLiterally(", ")
623
+ if exp.Max != nil {
624
+ if err := c.WriteValueExpr(exp.Max); err != nil {
625
+ return err
626
+ }
627
+ } else {
628
+ c.tsw.WriteLiterally("undefined")
629
+ }
630
+ }
631
+ c.tsw.WriteLiterally(")")
632
+ }
633
+ return nil
634
+ }
635
+
340
636
  // WriteKeyValueExpr translates a Go key-value pair expression (`ast.KeyValueExpr`),
341
637
  // typically found within composite literals (for structs, maps, or arrays with
342
638
  // 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",
@@ -76,16 +82,17 @@ func GoBuiltinToTypescript(typeName string) (string, bool) {
76
82
  // in their respective expression/statement writers and might not be directly mapped here.
77
83
  // Bitwise AND NOT (`&^=`) is also mapped but may require specific runtime support if not directly translatable.
78
84
  var tokenMap = map[token.Token]string{
79
- token.ADD: "+",
80
- token.SUB: "-",
81
- token.MUL: "*",
82
- token.QUO: "/",
83
- token.REM: "%",
84
- token.AND: "&",
85
- token.OR: "|",
86
- token.XOR: "^",
87
- token.SHL: "<<",
88
- token.SHR: ">>",
85
+ token.ADD: "+",
86
+ token.SUB: "-",
87
+ token.MUL: "*",
88
+ token.QUO: "/",
89
+ token.REM: "%",
90
+ token.AND: "&",
91
+ token.OR: "|",
92
+ token.XOR: "^",
93
+ token.SHL: "<<",
94
+ token.SHR: ">>",
95
+ token.AND_NOT: "& ~", // &^ operator: bitwise AND NOT
89
96
 
90
97
  token.ADD_ASSIGN: "+=",
91
98
  token.SUB_ASSIGN: "-=",