goscript 0.0.45 → 0.0.48

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.
@@ -374,68 +374,6 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
374
374
  }
375
375
  defer indexFile.Close() //nolint:errcheck
376
376
 
377
- // Collect exported symbols from all files in the package
378
- var exportedSymbols []string
379
-
380
- // Iterate through all syntax files to find exported symbols
381
- for i, syntax := range c.pkg.Syntax {
382
- fileName := c.pkg.CompiledGoFiles[i]
383
- baseFileName := filepath.Base(fileName)
384
- gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
385
-
386
- // Only include this file if it was compiled (in our compiledFiles list)
387
- fileWasCompiled := false
388
- for _, compiledFile := range compiledFiles {
389
- if compiledFile == gsFileName {
390
- fileWasCompiled = true
391
- break
392
- }
393
- }
394
- if !fileWasCompiled {
395
- continue
396
- }
397
-
398
- // Analyze declarations in this file to find exported symbols
399
- for _, decl := range syntax.Decls {
400
- switch d := decl.(type) {
401
- case *ast.FuncDecl:
402
- // Only include top-level functions (not methods)
403
- if d.Recv == nil && d.Name.IsExported() {
404
- exportedSymbols = append(exportedSymbols, d.Name.Name)
405
- }
406
- case *ast.GenDecl:
407
- for _, spec := range d.Specs {
408
- switch s := spec.(type) {
409
- case *ast.TypeSpec:
410
- if s.Name.IsExported() {
411
- exportedSymbols = append(exportedSymbols, s.Name.Name)
412
- }
413
- case *ast.ValueSpec:
414
- for _, name := range s.Names {
415
- if name.IsExported() {
416
- exportedSymbols = append(exportedSymbols, name.Name)
417
- }
418
- }
419
- }
420
- }
421
- }
422
- }
423
- }
424
-
425
- // Remove duplicates and sort
426
- symbolMap := make(map[string]bool)
427
- for _, symbol := range exportedSymbols {
428
- symbolMap[symbol] = true
429
- }
430
-
431
- var uniqueSymbols []string
432
- for symbol := range symbolMap {
433
- uniqueSymbols = append(uniqueSymbols, symbol)
434
- }
435
-
436
- // Sort for consistent output
437
- sort.Strings(uniqueSymbols)
438
-
439
377
  // Write selective re-exports for each compiled file
440
378
  for _, fileName := range compiledFiles {
441
379
  // Check if this is a protobuf file
@@ -452,7 +390,8 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
452
390
  }
453
391
 
454
392
  // Find which symbols this file exports
455
- var fileSymbols []string
393
+ var valueSymbols []string
394
+ var typeSymbols []string
456
395
 
457
396
  // Find the corresponding syntax file
458
397
  for i, syntax := range c.pkg.Syntax {
@@ -469,19 +408,21 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
469
408
  switch d := decl.(type) {
470
409
  case *ast.FuncDecl:
471
410
  if d.Recv == nil && d.Name.IsExported() {
472
- fileSymbols = append(fileSymbols, d.Name.Name)
411
+ valueSymbols = append(valueSymbols, d.Name.Name)
473
412
  }
474
413
  case *ast.GenDecl:
475
414
  for _, spec := range d.Specs {
476
415
  switch s := spec.(type) {
477
416
  case *ast.TypeSpec:
478
417
  if s.Name.IsExported() {
479
- fileSymbols = append(fileSymbols, s.Name.Name)
418
+ // All type declarations (interfaces, structs, type definitions, type aliases)
419
+ // become TypeScript types and must be exported with "export type"
420
+ typeSymbols = append(typeSymbols, s.Name.Name)
480
421
  }
481
422
  case *ast.ValueSpec:
482
423
  for _, name := range s.Names {
483
424
  if name.IsExported() {
484
- fileSymbols = append(fileSymbols, name.Name)
425
+ valueSymbols = append(valueSymbols, name.Name)
485
426
  }
486
427
  }
487
428
  }
@@ -491,11 +432,20 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
491
432
  break
492
433
  }
493
434
 
494
- // Write selective export if this file has exported symbols
495
- if len(fileSymbols) > 0 {
496
- sort.Strings(fileSymbols)
435
+ // Write exports if this file has exported symbols
436
+ if len(valueSymbols) > 0 {
437
+ sort.Strings(valueSymbols)
497
438
  exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
498
- strings.Join(fileSymbols, ", "), fileName)
439
+ strings.Join(valueSymbols, ", "), fileName)
440
+ if _, err := indexFile.WriteString(exportLine); err != nil {
441
+ return err
442
+ }
443
+ }
444
+
445
+ if len(typeSymbols) > 0 {
446
+ sort.Strings(typeSymbols)
447
+ exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
448
+ strings.Join(typeSymbols, ", "), fileName)
499
449
  if _, err := indexFile.WriteString(exportLine); err != nil {
500
450
  return err
501
451
  }
package/compiler/decl.go CHANGED
@@ -142,6 +142,49 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
142
142
  //
143
143
  // This function assumes it is called only for `FuncDecl` nodes that are methods.
144
144
  func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
145
+ _, err := c.writeMethodSignature(decl)
146
+ if err != nil {
147
+ return err
148
+ }
149
+
150
+ return c.writeMethodBodyWithReceiverBinding(decl, "this")
151
+ }
152
+
153
+ // writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
154
+ // It declares each named return variable with its appropriate type and zero value.
155
+ func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
156
+ if results == nil {
157
+ return nil
158
+ }
159
+
160
+ for _, field := range results.List {
161
+ for _, name := range field.Names {
162
+ c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
163
+ c.WriteTypeExpr(field.Type)
164
+ c.tsw.WriteLiterally(" = ")
165
+ c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
166
+ c.tsw.WriteLine("")
167
+ }
168
+ }
169
+ return nil
170
+ }
171
+
172
+ // hasNamedReturns checks if a function type has any named return parameters.
173
+ func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
174
+ if results == nil {
175
+ return false
176
+ }
177
+
178
+ for _, field := range results.List {
179
+ if len(field.Names) > 0 {
180
+ return true
181
+ }
182
+ }
183
+ return false
184
+ }
185
+
186
+ // writeMethodSignature writes the TypeScript method signature including async, public modifiers, name, parameters, and return type
187
+ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error) {
145
188
  if decl.Doc != nil {
146
189
  c.WriteDoc(decl.Doc)
147
190
  }
@@ -162,11 +205,10 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
162
205
 
163
206
  // Keep original Go casing for method names
164
207
  if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
165
- return err
208
+ return isAsync, err
166
209
  }
167
210
 
168
211
  // Write signature (parameters and return type)
169
- // We adapt the logic from WriteFuncType here, but without the 'function' keyword
170
212
  funcType := decl.Type
171
213
  c.tsw.WriteLiterally("(")
172
214
  if funcType.Params != nil {
@@ -208,8 +250,13 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
208
250
  }
209
251
 
210
252
  c.tsw.WriteLiterally(" ")
253
+ return isAsync, nil
254
+ }
211
255
 
212
- // Bind receiver name to this conditionally
256
+ // writeMethodBodyWithReceiverBinding writes the method body with optional receiver binding
257
+ // receiverTarget should be "this" for struct methods or "this._value" for named type methods
258
+ func (c *GoToTSCompiler) writeMethodBodyWithReceiverBinding(decl *ast.FuncDecl, receiverTarget string) error {
259
+ // Bind receiver name conditionally
213
260
  if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
214
261
  recvName := recvField.Names[0].Name
215
262
  if recvName != "_" {
@@ -225,7 +272,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
225
272
  if needsReceiverBinding {
226
273
  // Sanitize the receiver name to avoid conflicts with TypeScript reserved words
227
274
  sanitizedRecvName := c.sanitizeIdentifier(recvName)
228
- c.tsw.WriteLinef("const %s = this", sanitizedRecvName)
275
+ c.tsw.WriteLinef("const %s = %s", sanitizedRecvName, receiverTarget)
229
276
  }
230
277
 
231
278
  // Add using statement if needed
@@ -233,7 +280,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
233
280
  if c.analysis.IsInAsyncFunction(decl) {
234
281
  c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
235
282
  } else {
236
- c.tsw.WriteLine("using cleanup = new $.DisposableStack();")
283
+ c.tsw.WriteLine("using __defer = new $.DisposableStack();")
237
284
  }
238
285
  }
239
286
 
@@ -261,36 +308,3 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
261
308
 
262
309
  return nil
263
310
  }
264
-
265
- // writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
266
- // It declares each named return variable with its appropriate type and zero value.
267
- func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
268
- if results == nil {
269
- return nil
270
- }
271
-
272
- for _, field := range results.List {
273
- for _, name := range field.Names {
274
- c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
275
- c.WriteTypeExpr(field.Type)
276
- c.tsw.WriteLiterally(" = ")
277
- c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
278
- c.tsw.WriteLine("")
279
- }
280
- }
281
- return nil
282
- }
283
-
284
- // hasNamedReturns checks if a function type has any named return parameters.
285
- func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
286
- if results == nil {
287
- return false
288
- }
289
-
290
- for _, field := range results.List {
291
- if len(field.Names) > 0 {
292
- return true
293
- }
294
- }
295
- return false
296
- }
@@ -0,0 +1,101 @@
1
+ package compiler
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/types"
6
+ "strings"
7
+ )
8
+
9
+ // writeAsyncCall writes the await prefix for async function calls
10
+ func (c *GoToTSCompiler) writeAsyncCall(exp *ast.CallExpr, funIdent *ast.Ident) bool {
11
+ if funIdent == nil {
12
+ return false
13
+ }
14
+
15
+ // Check if this is an async function call
16
+ if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
17
+ c.tsw.WriteLiterally("await ")
18
+ return true
19
+ }
20
+
21
+ return false
22
+ }
23
+
24
+ // writeAsyncMethodCall writes the await prefix for async method calls
25
+ func (c *GoToTSCompiler) writeAsyncMethodCall(exp *ast.CallExpr) bool {
26
+ selExpr, ok := exp.Fun.(*ast.SelectorExpr)
27
+ if !ok {
28
+ return false
29
+ }
30
+
31
+ // Check if this is a method call on a variable (e.g., mu.Lock())
32
+ ident, ok := selExpr.X.(*ast.Ident)
33
+ if !ok {
34
+ return false
35
+ }
36
+
37
+ // Get the type of the receiver
38
+ obj := c.pkg.TypesInfo.Uses[ident]
39
+ if obj == nil {
40
+ return false
41
+ }
42
+
43
+ varObj, ok := obj.(*types.Var)
44
+ if !ok {
45
+ return false
46
+ }
47
+
48
+ // Get the type name and package
49
+ namedType, ok := varObj.Type().(*types.Named)
50
+ if !ok {
51
+ return false
52
+ }
53
+
54
+ typeName := namedType.Obj().Name()
55
+ methodName := selExpr.Sel.Name
56
+
57
+ // Check if the type is from an imported package
58
+ typePkg := namedType.Obj().Pkg()
59
+ if typePkg == nil || typePkg == c.pkg.Types {
60
+ return false
61
+ }
62
+
63
+ // Use the actual package name from the type information
64
+ pkgName := typePkg.Name()
65
+
66
+ // Check if this method is async based on metadata
67
+ if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
68
+ c.tsw.WriteLiterally("await ")
69
+ return true
70
+ }
71
+
72
+ return false
73
+ }
74
+
75
+ // addNonNullAssertion adds ! for function calls that might return null
76
+ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
77
+ if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
78
+ if _, ok := funType.Underlying().(*types.Signature); ok {
79
+ // Check if this is a function parameter identifier that needs not-null assertion
80
+ if ident, isIdent := expFun.(*ast.Ident); isIdent {
81
+ // Check if this identifier is a function parameter
82
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
83
+ if _, isVar := obj.(*types.Var); isVar {
84
+ // This is a variable (including function parameters)
85
+ // Function parameters that are function types need ! assertion
86
+ c.tsw.WriteLiterally("!")
87
+ }
88
+ }
89
+ } else if _, isNamed := funType.(*types.Named); isNamed {
90
+ c.tsw.WriteLiterally("!")
91
+ }
92
+ } else {
93
+ // Check if the function type is nullable (e.g., func(...) | null)
94
+ // This handles cases where a function call returns a nullable function
95
+ funTypeStr := funType.String()
96
+ if strings.Contains(funTypeStr, "| null") || strings.Contains(funTypeStr, "null |") {
97
+ c.tsw.WriteLiterally("!")
98
+ }
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,133 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+
8
+ "github.com/pkg/errors"
9
+ )
10
+
11
+ // writeBuiltinFunction handles built-in Go functions
12
+ func (c *GoToTSCompiler) writeBuiltinFunction(exp *ast.CallExpr, funName string) (handled bool, err error) {
13
+ switch funName {
14
+ case "panic":
15
+ c.tsw.WriteLiterally("$.panic")
16
+ return true, nil
17
+ case "println":
18
+ c.tsw.WriteLiterally("console.log")
19
+ return true, nil
20
+ case "len":
21
+ if len(exp.Args) != 1 {
22
+ return true, errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
23
+ }
24
+ c.tsw.WriteLiterally("$.len")
25
+ return true, nil
26
+ case "cap":
27
+ if len(exp.Args) != 1 {
28
+ return true, errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
29
+ }
30
+ c.tsw.WriteLiterally("$.cap")
31
+ return true, nil
32
+ case "new":
33
+ if len(exp.Args) != 1 {
34
+ return true, errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
35
+ }
36
+ c.tsw.WriteLiterally("new ")
37
+ c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
38
+ c.tsw.WriteLiterally("()")
39
+ return true, nil
40
+ case "delete":
41
+ if len(exp.Args) != 2 {
42
+ return true, errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
43
+ }
44
+ c.tsw.WriteLiterally("$.deleteMapEntry")
45
+ return true, nil
46
+ case "copy":
47
+ if len(exp.Args) != 2 {
48
+ return true, errors.Errorf("unhandled copy call with incorrect number of arguments: %d != 2", len(exp.Args))
49
+ }
50
+ c.tsw.WriteLiterally("$.copy")
51
+ return true, nil
52
+ case "recover":
53
+ if len(exp.Args) != 0 {
54
+ return true, errors.Errorf("unhandled recover call with incorrect number of arguments: %d != 0", len(exp.Args))
55
+ }
56
+ c.tsw.WriteLiterally("$.recover")
57
+ return true, nil
58
+ case "make":
59
+ return true, c.WriteCallExprMake(exp)
60
+ case "string":
61
+ return true, c.writeStringConversion(exp)
62
+ case "close":
63
+ if len(exp.Args) != 1 {
64
+ return true, errors.New("unhandled close call with incorrect number of arguments")
65
+ }
66
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
67
+ return true, fmt.Errorf("failed to write channel in close call: %w", err)
68
+ }
69
+ c.tsw.WriteLiterally(".close()")
70
+ return true, nil
71
+ case "append":
72
+ return true, c.writeAppendCall(exp)
73
+ case "byte":
74
+ if len(exp.Args) != 1 {
75
+ return true, errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
76
+ }
77
+ c.tsw.WriteLiterally("$.byte(")
78
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
79
+ return true, fmt.Errorf("failed to write argument for byte() conversion: %w", err)
80
+ }
81
+ c.tsw.WriteLiterally(")")
82
+ return true, nil
83
+ case "int":
84
+ return true, c.writeIntConversion(exp)
85
+ default:
86
+ return false, nil
87
+ }
88
+ }
89
+
90
+ // writeAppendCall handles append() function calls
91
+ func (c *GoToTSCompiler) writeAppendCall(exp *ast.CallExpr) error {
92
+ if len(exp.Args) < 1 {
93
+ return errors.New("unhandled append call with incorrect number of arguments")
94
+ }
95
+
96
+ c.tsw.WriteLiterally("$.append(")
97
+ // The first argument is the slice
98
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
99
+ return fmt.Errorf("failed to write slice in append call: %w", err)
100
+ }
101
+
102
+ // The remaining arguments are the elements to append
103
+ for i, arg := range exp.Args[1:] {
104
+ if i > 0 || len(exp.Args) > 1 {
105
+ c.tsw.WriteLiterally(", ")
106
+ }
107
+
108
+ // Special case: append([]byte, string...) should convert string to bytes
109
+ if exp.Ellipsis != token.NoPos && i == 0 { // This is the first element after slice and has ellipsis
110
+ // Check if the slice is []byte and the argument is a string
111
+ sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
112
+ argType := c.pkg.TypesInfo.TypeOf(arg)
113
+
114
+ if sliceType != nil && argType != nil {
115
+ if c.isByteSliceType(sliceType) && c.isStringType(argType) {
116
+ // Convert string to bytes: append([]byte, string...) -> $.append(slice, ...$.stringToBytes(string))
117
+ c.tsw.WriteLiterally("...$.stringToBytes(")
118
+ if err := c.WriteValueExpr(arg); err != nil {
119
+ return fmt.Errorf("failed to write string argument in append call: %w", err)
120
+ }
121
+ c.tsw.WriteLiterally(")")
122
+ continue
123
+ }
124
+ }
125
+ }
126
+
127
+ if err := c.WriteValueExpr(arg); err != nil {
128
+ return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
129
+ }
130
+ }
131
+ c.tsw.WriteLiterally(")")
132
+ return nil
133
+ }
@@ -0,0 +1,138 @@
1
+ package compiler
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/types"
6
+
7
+ "github.com/pkg/errors"
8
+ )
9
+
10
+ // isByteSliceType checks if a type is []byte (slice of uint8)
11
+ func (c *GoToTSCompiler) isByteSliceType(t types.Type) bool {
12
+ if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
13
+ if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
14
+ return true
15
+ }
16
+ }
17
+ return false
18
+ }
19
+
20
+ // isRuneSliceType checks if a type is []rune (slice of int32)
21
+ func (c *GoToTSCompiler) isRuneSliceType(t types.Type) bool {
22
+ if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
23
+ if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Int32 {
24
+ return true
25
+ }
26
+ }
27
+ return false
28
+ }
29
+
30
+ // isStringType checks if a type is string
31
+ func (c *GoToTSCompiler) isStringType(t types.Type) bool {
32
+ if basic, isBasic := t.Underlying().(*types.Basic); isBasic {
33
+ return basic.Kind() == types.String || basic.Kind() == types.UntypedString
34
+ }
35
+ return false
36
+ }
37
+
38
+ // writeByteSliceCreation handles the creation of []byte slices with proper Uint8Array handling
39
+ func (c *GoToTSCompiler) writeByteSliceCreation(lengthArg, capacityArg interface{}) error {
40
+ return c.writeSliceCreationForType(lengthArg, capacityArg, true)
41
+ }
42
+
43
+ // writeSliceCreationForType handles slice creation with special handling for byte slices
44
+ func (c *GoToTSCompiler) writeSliceCreationForType(lengthArg, capacityArg interface{}, isByteSlice bool) error {
45
+ hasCapacity := capacityArg != nil
46
+
47
+ if isByteSlice && !hasCapacity {
48
+ // make([]byte, len) - capacity equals length, use Uint8Array
49
+ c.tsw.WriteLiterally("new Uint8Array(")
50
+ if err := c.writeExprOrDefault(lengthArg, "0"); err != nil {
51
+ return err
52
+ }
53
+ c.tsw.WriteLiterally(")")
54
+ return nil
55
+ }
56
+
57
+ // Use $.makeSlice for all other cases
58
+ if isByteSlice {
59
+ c.tsw.WriteLiterally("$.makeSlice<number>(")
60
+ } else {
61
+ return errors.New("writeSliceCreationForType called for non-byte slice without element type")
62
+ }
63
+
64
+ if err := c.writeExprOrDefault(lengthArg, "0"); err != nil {
65
+ return err
66
+ }
67
+
68
+ if hasCapacity {
69
+ c.tsw.WriteLiterally(", ")
70
+ if err := c.writeExprOrDefault(capacityArg, "0"); err != nil {
71
+ return err
72
+ }
73
+ }
74
+
75
+ if isByteSlice {
76
+ c.tsw.WriteLiterally(", 'byte')")
77
+ }
78
+
79
+ return nil
80
+ }
81
+
82
+ // writeGenericSliceCreation handles the creation of generic slices with proper type hints
83
+ func (c *GoToTSCompiler) writeGenericSliceCreation(elemType types.Type, lengthArg, capacityArg interface{}) error {
84
+ hasCapacity := capacityArg != nil
85
+
86
+ c.tsw.WriteLiterally("$.makeSlice<")
87
+ c.WriteGoType(elemType, GoTypeContextGeneral)
88
+ c.tsw.WriteLiterally(">(")
89
+
90
+ if err := c.writeExprOrDefault(lengthArg, "0"); err != nil {
91
+ return err
92
+ }
93
+
94
+ if hasCapacity {
95
+ c.tsw.WriteLiterally(", ")
96
+ if err := c.writeExprOrDefault(capacityArg, "0"); err != nil {
97
+ return err
98
+ }
99
+ }
100
+
101
+ // Add type hint for proper zero value initialization
102
+ c.writeSliceTypeHint(elemType, hasCapacity)
103
+ c.tsw.WriteLiterally(")")
104
+ return nil
105
+ }
106
+
107
+ // writeSliceTypeHint writes the type hint parameter for makeSlice calls
108
+ func (c *GoToTSCompiler) writeSliceTypeHint(elemType types.Type, hasCapacity bool) {
109
+ typeHint := c.getTypeHintForSliceElement(elemType)
110
+ if typeHint != "" {
111
+ if !hasCapacity {
112
+ c.tsw.WriteLiterally(", undefined")
113
+ }
114
+ c.tsw.WriteLiterally(", '")
115
+ c.tsw.WriteLiterally(typeHint)
116
+ c.tsw.WriteLiterally("'")
117
+ }
118
+ }
119
+
120
+ // writeExprOrDefault writes an expression if it's not nil, otherwise writes a default value
121
+ func (c *GoToTSCompiler) writeExprOrDefault(expr interface{}, defaultValue string) error {
122
+ if expr == nil {
123
+ c.tsw.WriteLiterally(defaultValue)
124
+ return nil
125
+ }
126
+
127
+ switch e := expr.(type) {
128
+ case string:
129
+ c.tsw.WriteLiterally(e)
130
+ return nil
131
+ case ast.Expr:
132
+ // If it's an ast.Expr, call WriteValueExpr directly
133
+ return c.WriteValueExpr(e)
134
+ default:
135
+ // If we can't handle the type, return an error
136
+ return errors.Errorf("unsupported expression type in writeExprOrDefault: %T", e)
137
+ }
138
+ }