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.
@@ -5,9 +5,6 @@ import (
5
5
  "go/ast"
6
6
  "go/token"
7
7
  "go/types"
8
- "strings"
9
-
10
- "github.com/pkg/errors"
11
8
  )
12
9
 
13
10
  // WriteCallExpr translates a Go function call expression (`ast.CallExpr`)
@@ -25,7 +22,7 @@ import (
25
22
  // - `string(runeVal)` becomes `$.runeOrStringToString(runeVal)`.
26
23
  // - `string([]runeVal)` becomes `$.runesToString(sliceVal)`.
27
24
  // - `string([]byteVal)` becomes `$.bytesToString(sliceVal)`.
28
- // - `[]rune(stringVal)` becomes `$.stringToRunes(stringVal)“.
25
+ // - `[]rune(stringVal)` becomes `$.stringToRunes(stringVal)".
29
26
  // - `[]byte(stringVal)` becomes `$.stringToBytes(stringVal)`.
30
27
  // - `close(ch)` becomes `ch.close()`.
31
28
  // - `append(slice, elems...)` becomes `$.append(slice, elems...)`.
@@ -45,1221 +42,86 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
45
42
  }
46
43
 
47
44
  // Handle any type conversion with nil argument
48
- if len(exp.Args) == 1 {
49
- if nilIdent, isIdent := exp.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
50
- // Handle nil pointer to struct type conversions: (*struct{})(nil)
51
- if starExpr, isStarExpr := expFun.(*ast.StarExpr); isStarExpr {
52
- if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
53
- c.tsw.WriteLiterally("null")
54
- return nil
55
- }
56
- }
57
-
58
- c.tsw.WriteLiterally("null")
59
- return nil
60
- }
45
+ if handled, err := c.writeNilConversion(exp); handled {
46
+ return err
61
47
  }
62
48
 
63
49
  // Handle array type conversions like []rune(string)
64
- if arrayType, isArrayType := expFun.(*ast.ArrayType); isArrayType {
65
- // Check if it's a []rune type
66
- if ident, isIdent := arrayType.Elt.(*ast.Ident); isIdent && ident.Name == "rune" {
67
- // Check if the argument is a string
68
- if len(exp.Args) == 1 {
69
- arg := exp.Args[0]
70
- if tv, ok := c.pkg.TypesInfo.Types[arg]; ok && tv.Type != nil {
71
- if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
72
- // Translate []rune(stringValue) to $.stringToRunes(stringValue)
73
- c.tsw.WriteLiterally("$.stringToRunes(")
74
- if err := c.WriteValueExpr(arg); err != nil {
75
- return fmt.Errorf("failed to write argument for []rune(string) conversion: %w", err)
76
- }
77
- c.tsw.WriteLiterally(")")
78
- return nil // Handled []rune(string)
79
- }
80
- }
81
- }
82
- }
83
- // Check if it's a []byte type and the argument is a string
84
- if eltIdent, ok := arrayType.Elt.(*ast.Ident); ok && eltIdent.Name == "byte" && arrayType.Len == nil {
85
- if len(exp.Args) == 1 {
86
- arg := exp.Args[0]
87
- // Ensure TypesInfo is available and the argument type can be determined
88
- if tv, typeOk := c.pkg.TypesInfo.Types[arg]; typeOk && tv.Type != nil {
89
- if basicArgType, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basicArgType.Info()&types.IsString) != 0 {
90
- c.tsw.WriteLiterally("$.stringToBytes(")
91
- if err := c.WriteValueExpr(arg); err != nil {
92
- return fmt.Errorf("failed to write argument for []byte(string) conversion: %w", err)
93
- }
94
- c.tsw.WriteLiterally(")")
95
- return nil // Handled []byte(string)
96
- }
97
- }
98
- }
99
- }
100
-
101
- // Handle general slice type conversions like []T(namedType) where namedType has underlying type []T
102
- if arrayType.Len == nil && len(exp.Args) == 1 {
103
- arg := exp.Args[0]
104
- if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
105
- // Check if the argument is a named type with a slice underlying type
106
- if namedArgType, isNamed := argType.(*types.Named); isNamed {
107
- // Check if the named type has receiver methods (is a wrapper class)
108
- typeName := namedArgType.Obj().Name()
109
- if c.hasReceiverMethods(typeName) {
110
- // Check if the underlying type matches the target slice type
111
- if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
112
- // Get the target slice type
113
- targetType := c.pkg.TypesInfo.TypeOf(arrayType)
114
- if targetSliceType, isTargetSlice := targetType.Underlying().(*types.Slice); isTargetSlice {
115
- // Check if element types are compatible
116
- if types.Identical(sliceUnderlying.Elem(), targetSliceType.Elem()) {
117
- // This is a conversion from NamedType to []T where NamedType has underlying []T
118
- // Use valueOf() to get the underlying slice
119
- if err := c.WriteValueExpr(arg); err != nil {
120
- return fmt.Errorf("failed to write argument for slice type conversion: %w", err)
121
- }
122
- c.tsw.WriteLiterally(".valueOf()")
123
- return nil // Handled named type to slice conversion
124
- }
125
- }
126
- }
127
- }
128
- }
129
- }
130
- }
50
+ if handled, err := c.writeArrayTypeConversion(exp); handled {
51
+ return err
131
52
  }
132
53
 
54
+ // Handle built-in functions called as identifiers
133
55
  if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
134
- switch funIdent.String() {
135
- case "panic":
136
- c.tsw.WriteLiterally("$.panic")
137
- case "println":
138
- c.tsw.WriteLiterally("console.log")
139
- case "len":
140
- // Translate len(arg) to $.len(arg)
141
- if len(exp.Args) != 1 {
142
- return errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
56
+ // Check for built-in functions first
57
+ if handled, err := c.writeBuiltinFunction(exp, funIdent.String()); handled {
58
+ if err != nil {
59
+ return err
143
60
  }
144
- c.tsw.WriteLiterally("$.len")
145
- case "cap":
146
- // Translate cap(arg) to $.cap(arg)
147
- if len(exp.Args) != 1 {
148
- return errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
61
+ // For built-ins that don't return early, write the arguments
62
+ if funIdent.String() != "new" && funIdent.String() != "close" && funIdent.String() != "make" &&
63
+ funIdent.String() != "string" && funIdent.String() != "append" && funIdent.String() != "byte" &&
64
+ funIdent.String() != "int" {
65
+ return c.writeCallArguments(exp)
149
66
  }
150
- c.tsw.WriteLiterally("$.cap")
151
- case "new":
152
- // Translate new(T) to new T_ts()
153
- if len(exp.Args) != 1 {
154
- return errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
155
- }
156
- c.tsw.WriteLiterally("new ")
157
- c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
158
- c.tsw.WriteLiterally("()")
159
- return nil // Prevent falling through to generic argument handling
160
- case "delete":
161
- // Translate delete(map, key) to $.deleteMapEntry(map, key)
162
- if len(exp.Args) != 2 {
163
- return errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
164
- }
165
- c.tsw.WriteLiterally("$.deleteMapEntry")
166
- case "copy":
167
- // Translate copy(dst, src) to $.copy(dst, src)
168
- if len(exp.Args) != 2 {
169
- return errors.Errorf("unhandled copy call with incorrect number of arguments: %d != 2", len(exp.Args))
170
- }
171
- c.tsw.WriteLiterally("$.copy")
172
- case "recover":
173
- // Translate recover() to $.recover()
174
- if len(exp.Args) != 0 {
175
- return errors.Errorf("unhandled recover call with incorrect number of arguments: %d != 0", len(exp.Args))
176
- }
177
- c.tsw.WriteLiterally("$.recover")
178
- case "make":
179
- // First check if we have a channel type
180
- if typ := c.pkg.TypesInfo.TypeOf(exp.Args[0]); typ != nil {
181
- if chanType, ok := typ.Underlying().(*types.Chan); ok {
182
- // Handle channel creation: make(chan T, bufferSize) or make(chan T)
183
- c.tsw.WriteLiterally("$.makeChannel<")
184
- c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
185
- c.tsw.WriteLiterally(">(")
186
-
187
- // If buffer size is provided, add it
188
- if len(exp.Args) >= 2 {
189
- if err := c.WriteValueExpr(exp.Args[1]); err != nil {
190
- return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
191
- }
192
- } else {
193
- // Default to 0 (unbuffered channel)
194
- c.tsw.WriteLiterally("0")
195
- }
196
-
197
- c.tsw.WriteLiterally(", ") // Add comma for zero value argument
198
-
199
- // Write the zero value for the channel's element type
200
- if chanType.Elem().String() == "struct{}" {
201
- c.tsw.WriteLiterally("{}")
202
- } else {
203
- c.WriteZeroValueForType(chanType.Elem())
204
- }
205
-
206
- // Add direction parameter
207
- c.tsw.WriteLiterally(", ")
208
-
209
- // Determine channel direction
210
- switch chanType.Dir() {
211
- case types.SendRecv:
212
- c.tsw.WriteLiterally("'both'")
213
- case types.SendOnly:
214
- c.tsw.WriteLiterally("'send'")
215
- case types.RecvOnly:
216
- c.tsw.WriteLiterally("'receive'")
217
- default:
218
- c.tsw.WriteLiterally("'both'") // Default to bidirectional
219
- }
220
-
221
- c.tsw.WriteLiterally(")")
222
- return nil // Handled make for channel
223
- }
224
- }
225
- // Handle make for slices: make([]T, len, cap) or make([]T, len)
226
- if len(exp.Args) >= 1 {
227
- // Handle map creation: make(map[K]V)
228
- if mapType, ok := exp.Args[0].(*ast.MapType); ok {
229
- c.tsw.WriteLiterally("$.makeMap<")
230
- c.WriteTypeExpr(mapType.Key) // Write the key type
231
- c.tsw.WriteLiterally(", ")
232
- c.WriteTypeExpr(mapType.Value) // Write the value type
233
- c.tsw.WriteLiterally(">()")
234
- return nil // Handled make for map
235
- }
236
-
237
- // Handle slice creation
238
- if _, ok := exp.Args[0].(*ast.ArrayType); ok {
239
- // Get the slice type information
240
- sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
241
- if sliceType == nil {
242
- return errors.New("could not get type information for slice in make call")
243
- }
244
- goUnderlyingType, ok := sliceType.Underlying().(*types.Slice)
245
- if !ok {
246
- return errors.New("expected slice type for make call")
247
- }
248
- goElemType := goUnderlyingType.Elem()
249
-
250
- // Check if it's make([]byte, ...)
251
- if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
252
- // Check if capacity is different from length
253
- if len(exp.Args) == 3 {
254
- // make([]byte, len, cap) - need to handle capacity
255
- c.tsw.WriteLiterally("$.makeSlice<number>(")
256
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
257
- return err
258
- }
259
- c.tsw.WriteLiterally(", ")
260
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
261
- return err
262
- }
263
- c.tsw.WriteLiterally(", 'byte')")
264
- } else {
265
- // make([]byte, len) - capacity equals length, use Uint8Array
266
- c.tsw.WriteLiterally("new Uint8Array(")
267
- if len(exp.Args) >= 2 {
268
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
269
- return err
270
- }
271
- } else {
272
- // If no length is provided, default to 0
273
- c.tsw.WriteLiterally("0")
274
- }
275
- c.tsw.WriteLiterally(")")
276
- }
277
- return nil // Handled make for []byte
278
- }
279
-
280
- // Check if the element type is a generic type parameter
281
- if _, isTypeParam := goElemType.(*types.TypeParam); isTypeParam {
282
- // This is make([]E, n) where E is a type parameter
283
- c.tsw.WriteLiterally("$.makeSlice<")
284
- c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type parameter
285
- c.tsw.WriteLiterally(">(")
286
-
287
- if len(exp.Args) >= 2 {
288
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
289
- return err
290
- }
291
- if len(exp.Args) == 3 {
292
- c.tsw.WriteLiterally(", ")
293
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
294
- return err
295
- }
296
- } else if len(exp.Args) > 3 {
297
- return errors.New("makeSlice expects 2 or 3 arguments")
298
- }
299
- } else {
300
- // If no length is provided, default to 0
301
- c.tsw.WriteLiterally("0")
302
- }
303
- c.tsw.WriteLiterally(")")
304
- return nil // Handled make for []E where E is type parameter
305
- }
306
-
307
- c.tsw.WriteLiterally("$.makeSlice<")
308
- // Use AST-based type writing to preserve qualified names
309
- if arrType, ok := exp.Args[0].(*ast.ArrayType); ok {
310
- c.WriteTypeExpr(arrType.Elt)
311
- } else {
312
- c.WriteGoType(goElemType, GoTypeContextGeneral)
313
- }
314
- c.tsw.WriteLiterally(">(")
315
-
316
- hasCapacity := len(exp.Args) == 3
317
-
318
- if len(exp.Args) >= 2 {
319
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
320
- return err
321
- }
322
- if hasCapacity {
323
- c.tsw.WriteLiterally(", ")
324
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
325
- return err
326
- }
327
- } else if len(exp.Args) > 3 {
328
- return errors.New("makeSlice expects 2 or 3 arguments")
329
- }
330
- } else {
331
- // If no length is provided, default to 0
332
- c.tsw.WriteLiterally("0")
333
- }
334
-
335
- // Add type hint for proper zero value initialization
336
- typeHint := c.getTypeHintForSliceElement(goElemType)
337
- if typeHint != "" {
338
- if !hasCapacity {
339
- // If no capacity was provided, add undefined for capacity parameter
340
- c.tsw.WriteLiterally(", undefined")
341
- }
342
- c.tsw.WriteLiterally(", '")
343
- c.tsw.WriteLiterally(typeHint)
344
- c.tsw.WriteLiterally("'")
345
- }
346
-
347
- c.tsw.WriteLiterally(")")
348
- return nil // Handled make for slice
349
- }
350
-
351
- // Handle generic type parameter make calls: make(S, len, cap) where S ~[]E
352
- if ident, ok := exp.Args[0].(*ast.Ident); ok {
353
- // Check if this identifier refers to a type parameter
354
- if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
355
- if typeName, isTypeName := obj.(*types.TypeName); isTypeName {
356
- if typeParam, isTypeParam := typeName.Type().(*types.TypeParam); isTypeParam {
357
- // Check if the type parameter is constrained to slice types
358
- constraint := typeParam.Constraint()
359
- if constraint != nil {
360
- underlying := constraint.Underlying()
361
- if iface, isInterface := underlying.(*types.Interface); isInterface {
362
- // Check if the constraint includes slice types
363
- // For constraints like ~[]E, we need to look at the type terms
364
- if hasSliceConstraint(iface) {
365
- // This is a generic slice type parameter
366
- // We need to determine the element type from the constraint
367
- elemType := getSliceElementTypeFromConstraint(iface)
368
- if elemType != nil {
369
- // Check if it's make(S, ...) where S constrains to []byte
370
- if basicElem, isBasic := elemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
371
- // Check if capacity is different from length
372
- if len(exp.Args) == 3 {
373
- // make([]byte, len, cap) - need to handle capacity
374
- c.tsw.WriteLiterally("$.makeSlice<number>(")
375
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
376
- return err
377
- }
378
- c.tsw.WriteLiterally(", ")
379
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
380
- return err
381
- }
382
- c.tsw.WriteLiterally(", 'byte')")
383
- } else {
384
- // make([]byte, len) - capacity equals length, use Uint8Array
385
- c.tsw.WriteLiterally("new Uint8Array(")
386
- if len(exp.Args) >= 2 {
387
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
388
- return err
389
- }
390
- } else {
391
- // If no length is provided, default to 0
392
- c.tsw.WriteLiterally("0")
393
- }
394
- c.tsw.WriteLiterally(")")
395
- }
396
- return nil // Handled make for generic []byte
397
- }
398
-
399
- c.tsw.WriteLiterally("$.makeSlice<")
400
- c.WriteGoType(elemType, GoTypeContextGeneral) // Write the element type
401
- c.tsw.WriteLiterally(">(")
402
-
403
- hasCapacity := len(exp.Args) == 3
404
-
405
- if len(exp.Args) >= 2 {
406
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
407
- return err
408
- }
409
- if hasCapacity {
410
- c.tsw.WriteLiterally(", ")
411
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
412
- return err
413
- }
414
- } else if len(exp.Args) > 3 {
415
- return errors.New("makeSlice expects 2 or 3 arguments")
416
- }
417
- } else {
418
- // If no length is provided, default to 0
419
- c.tsw.WriteLiterally("0")
420
- }
421
-
422
- // Add type hint for proper zero value initialization
423
- typeHint := c.getTypeHintForSliceElement(elemType)
424
- if typeHint != "" {
425
- if !hasCapacity {
426
- // If no capacity was provided, add undefined for capacity parameter
427
- c.tsw.WriteLiterally(", undefined")
428
- }
429
- c.tsw.WriteLiterally(", '")
430
- c.tsw.WriteLiterally(typeHint)
431
- c.tsw.WriteLiterally("'")
432
- }
433
-
434
- c.tsw.WriteLiterally(")")
435
- return nil // Handled make for generic slice
436
- }
437
- }
438
- }
439
- }
440
- } else {
441
- // Handle named types with slice underlying types: make(NamedSliceType, len, cap)
442
- // This handles cases like: type appendSliceWriter []byte; make(appendSliceWriter, 0, len(s))
443
- namedType := typeName.Type()
444
- if sliceType, isSlice := namedType.Underlying().(*types.Slice); isSlice {
445
- goElemType := sliceType.Elem()
446
-
447
- // Check if it's a named type with []byte underlying type
448
- if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
449
- // Check if capacity is different from length
450
- if len(exp.Args) == 3 {
451
- // make([]byte, len, cap) - need to handle capacity
452
- c.tsw.WriteLiterally("$.makeSlice<number>(")
453
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
454
- return err
455
- }
456
- c.tsw.WriteLiterally(", ")
457
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
458
- return err
459
- }
460
- c.tsw.WriteLiterally(", 'byte')")
461
- } else {
462
- // make([]byte, len) - capacity equals length, use Uint8Array
463
- c.tsw.WriteLiterally("new Uint8Array(")
464
- if len(exp.Args) >= 2 {
465
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
466
- return err
467
- }
468
- } else {
469
- // If no length is provided, default to 0
470
- c.tsw.WriteLiterally("0")
471
- }
472
- c.tsw.WriteLiterally(")")
473
- }
474
- return nil // Handled make for named []byte type
475
- }
476
-
477
- // Handle other named slice types
478
- c.tsw.WriteLiterally("$.makeSlice<")
479
- c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
480
- c.tsw.WriteLiterally(">(")
481
-
482
- hasCapacity := len(exp.Args) == 3
483
-
484
- if len(exp.Args) >= 2 {
485
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
486
- return err
487
- }
488
- if hasCapacity {
489
- c.tsw.WriteLiterally(", ")
490
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
491
- return err
492
- }
493
- } else if len(exp.Args) > 3 {
494
- return errors.New("makeSlice expects 2 or 3 arguments")
495
- }
496
- } else {
497
- // If no length is provided, default to 0
498
- c.tsw.WriteLiterally("0")
499
- }
500
-
501
- // Add type hint for proper zero value initialization
502
- typeHint := c.getTypeHintForSliceElement(goElemType)
503
- if typeHint != "" {
504
- if !hasCapacity {
505
- // If no capacity was provided, add undefined for capacity parameter
506
- c.tsw.WriteLiterally(", undefined")
507
- }
508
- c.tsw.WriteLiterally(", '")
509
- c.tsw.WriteLiterally(typeHint)
510
- c.tsw.WriteLiterally("'")
511
- }
512
-
513
- c.tsw.WriteLiterally(")")
514
- return nil // Handled make for named slice type
515
- }
516
-
517
- // Handle named types with map underlying types: make(NamedMapType)
518
- if mapType, isMap := namedType.Underlying().(*types.Map); isMap {
519
- c.tsw.WriteLiterally("$.makeMap<")
520
- c.WriteGoType(mapType.Key(), GoTypeContextGeneral) // Write the key type
521
- c.tsw.WriteLiterally(", ")
522
- c.WriteGoType(mapType.Elem(), GoTypeContextGeneral) // Write the value type
523
- c.tsw.WriteLiterally(">()")
524
- return nil // Handled make for named map type
525
- }
526
-
527
- // Handle named types with channel underlying types: make(NamedChannelType, bufferSize)
528
- if chanType, isChan := namedType.Underlying().(*types.Chan); isChan {
529
- c.tsw.WriteLiterally("$.makeChannel<")
530
- c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
531
- c.tsw.WriteLiterally(">(")
532
-
533
- // If buffer size is provided, add it
534
- if len(exp.Args) >= 2 {
535
- if err := c.WriteValueExpr(exp.Args[1]); err != nil {
536
- return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
537
- }
538
- } else {
539
- // Default to 0 (unbuffered channel)
540
- c.tsw.WriteLiterally("0")
541
- }
542
-
543
- c.tsw.WriteLiterally(", ") // Add comma for zero value argument
544
-
545
- // Write the zero value for the channel's element type
546
- if chanType.Elem().String() == "struct{}" {
547
- c.tsw.WriteLiterally("{}")
548
- } else {
549
- c.WriteZeroValueForType(chanType.Elem())
550
- }
551
-
552
- // Add direction parameter
553
- c.tsw.WriteLiterally(", ")
554
-
555
- // Determine channel direction
556
- switch chanType.Dir() {
557
- case types.SendRecv:
558
- c.tsw.WriteLiterally("'both'")
559
- case types.SendOnly:
560
- c.tsw.WriteLiterally("'send'")
561
- case types.RecvOnly:
562
- c.tsw.WriteLiterally("'receive'")
563
- default:
564
- c.tsw.WriteLiterally("'both'") // Default to bidirectional
565
- }
566
-
567
- c.tsw.WriteLiterally(")")
568
- return nil // Handled make for named channel type
569
- }
570
- }
571
- }
572
- }
573
- }
574
- }
575
- // Handle instantiated generic types: make(GenericType[TypeArg], ...)
576
- // This handles cases like: make(Ints[int64]) where Ints[T] is a generic type
577
- if indexExpr, ok := exp.Args[0].(*ast.IndexExpr); ok {
578
- // Get the type information for the instantiated generic type
579
- if typ := c.pkg.TypesInfo.TypeOf(indexExpr); typ != nil {
580
- // Check the underlying type of the instantiated generic type
581
- underlying := typ.Underlying()
582
-
583
- // Handle instantiated generic map types: make(GenericMap[K, V])
584
- if mapType, isMap := underlying.(*types.Map); isMap {
585
- c.tsw.WriteLiterally("$.makeMap<")
586
- c.WriteGoType(mapType.Key(), GoTypeContextGeneral) // Write the key type
587
- c.tsw.WriteLiterally(", ")
588
- c.WriteGoType(mapType.Elem(), GoTypeContextGeneral) // Write the value type
589
- c.tsw.WriteLiterally(">()")
590
- return nil // Handled make for instantiated generic map type
591
- }
592
-
593
- // Handle instantiated generic slice types: make(GenericSlice[T], len, cap)
594
- if sliceType, isSlice := underlying.(*types.Slice); isSlice {
595
- goElemType := sliceType.Elem()
596
-
597
- // Check if it's an instantiated generic type with []byte underlying type
598
- if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
599
- // Check if capacity is different from length
600
- if len(exp.Args) == 3 {
601
- // make([]byte, len, cap) - need to handle capacity
602
- c.tsw.WriteLiterally("$.makeSlice<number>(")
603
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
604
- return err
605
- }
606
- c.tsw.WriteLiterally(", ")
607
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
608
- return err
609
- }
610
- c.tsw.WriteLiterally(", 'byte')")
611
- } else {
612
- // make([]byte, len) - capacity equals length, use Uint8Array
613
- c.tsw.WriteLiterally("new Uint8Array(")
614
- if len(exp.Args) >= 2 {
615
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
616
- return err
617
- }
618
- } else {
619
- // If no length is provided, default to 0
620
- c.tsw.WriteLiterally("0")
621
- }
622
- c.tsw.WriteLiterally(")")
623
- }
624
- return nil // Handled make for instantiated generic []byte type
625
- }
626
-
627
- // Handle other instantiated generic slice types
628
- c.tsw.WriteLiterally("$.makeSlice<")
629
- c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
630
- c.tsw.WriteLiterally(">(")
631
-
632
- hasCapacity := len(exp.Args) == 3
633
-
634
- if len(exp.Args) >= 2 {
635
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
636
- return err
637
- }
638
- if hasCapacity {
639
- c.tsw.WriteLiterally(", ")
640
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
641
- return err
642
- }
643
- } else if len(exp.Args) > 3 {
644
- return errors.New("makeSlice expects 2 or 3 arguments")
645
- }
646
- } else {
647
- // If no length is provided, default to 0
648
- c.tsw.WriteLiterally("0")
649
- }
650
-
651
- // Add type hint for proper zero value initialization
652
- typeHint := c.getTypeHintForSliceElement(goElemType)
653
- if typeHint != "" {
654
- if !hasCapacity {
655
- // If no capacity was provided, add undefined for capacity parameter
656
- c.tsw.WriteLiterally(", undefined")
657
- }
658
- c.tsw.WriteLiterally(", '")
659
- c.tsw.WriteLiterally(typeHint)
660
- c.tsw.WriteLiterally("'")
661
- }
662
-
663
- c.tsw.WriteLiterally(")")
664
- return nil // Handled make for instantiated generic slice type
665
- }
666
-
667
- // Handle instantiated generic channel types: make(GenericChannel[T], bufferSize)
668
- if chanType, isChan := underlying.(*types.Chan); isChan {
669
- c.tsw.WriteLiterally("$.makeChannel<")
670
- c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
671
- c.tsw.WriteLiterally(">(")
672
-
673
- // If buffer size is provided, add it
674
- if len(exp.Args) >= 2 {
675
- if err := c.WriteValueExpr(exp.Args[1]); err != nil {
676
- return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
677
- }
678
- } else {
679
- // Default to 0 (unbuffered channel)
680
- c.tsw.WriteLiterally("0")
681
- }
682
-
683
- c.tsw.WriteLiterally(", ") // Add comma for zero value argument
684
-
685
- // Write the zero value for the channel's element type
686
- if chanType.Elem().String() == "struct{}" {
687
- c.tsw.WriteLiterally("{}")
688
- } else {
689
- c.WriteZeroValueForType(chanType.Elem())
690
- }
691
-
692
- // Add direction parameter
693
- c.tsw.WriteLiterally(", ")
694
-
695
- // Determine channel direction
696
- switch chanType.Dir() {
697
- case types.SendRecv:
698
- c.tsw.WriteLiterally("'both'")
699
- case types.SendOnly:
700
- c.tsw.WriteLiterally("'send'")
701
- case types.RecvOnly:
702
- c.tsw.WriteLiterally("'receive'")
703
- default:
704
- c.tsw.WriteLiterally("'both'") // Default to bidirectional
705
- }
706
-
707
- c.tsw.WriteLiterally(")")
708
- return nil // Handled make for instantiated generic channel type
709
- }
710
- }
711
- }
712
- // Handle selector expressions: make(pkg.TypeName, ...)
713
- // This handles cases like: make(fstest.MapFS) where fstest.MapFS is map[string]*MapFile
714
- if selectorExpr, ok := exp.Args[0].(*ast.SelectorExpr); ok {
715
- // Get the type information for the selector expression
716
- if typ := c.pkg.TypesInfo.TypeOf(selectorExpr); typ != nil {
717
- // Check the underlying type of the selector expression
718
- underlying := typ.Underlying()
719
-
720
- // Handle selector expression map types: make(pkg.MapType)
721
- if mapType, isMap := underlying.(*types.Map); isMap {
722
- c.tsw.WriteLiterally("$.makeMap<")
723
- c.WriteGoType(mapType.Key(), GoTypeContextGeneral) // Write the key type
724
- c.tsw.WriteLiterally(", ")
725
- c.WriteGoType(mapType.Elem(), GoTypeContextGeneral) // Write the value type
726
- c.tsw.WriteLiterally(">()")
727
- return nil // Handled make for selector expression map type
728
- }
729
-
730
- // Handle selector expression slice types: make(pkg.SliceType, len, cap)
731
- if sliceType, isSlice := underlying.(*types.Slice); isSlice {
732
- goElemType := sliceType.Elem()
733
-
734
- // Check if it's a selector expression with []byte underlying type
735
- if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
736
- // Check if capacity is different from length
737
- if len(exp.Args) == 3 {
738
- // make([]byte, len, cap) - need to handle capacity
739
- c.tsw.WriteLiterally("$.makeSlice<number>(")
740
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
741
- return err
742
- }
743
- c.tsw.WriteLiterally(", ")
744
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
745
- return err
746
- }
747
- c.tsw.WriteLiterally(", 'byte')")
748
- } else {
749
- // make([]byte, len) - capacity equals length, use Uint8Array
750
- c.tsw.WriteLiterally("new Uint8Array(")
751
- if len(exp.Args) >= 2 {
752
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
753
- return err
754
- }
755
- } else {
756
- // If no length is provided, default to 0
757
- c.tsw.WriteLiterally("0")
758
- }
759
- c.tsw.WriteLiterally(")")
760
- }
761
- return nil // Handled make for selector expression []byte type
762
- }
763
-
764
- // Handle other selector expression slice types
765
- c.tsw.WriteLiterally("$.makeSlice<")
766
- c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
767
- c.tsw.WriteLiterally(">(")
768
-
769
- hasCapacity := len(exp.Args) == 3
770
-
771
- if len(exp.Args) >= 2 {
772
- if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
773
- return err
774
- }
775
- if hasCapacity {
776
- c.tsw.WriteLiterally(", ")
777
- if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
778
- return err
779
- }
780
- } else if len(exp.Args) > 3 {
781
- return errors.New("makeSlice expects 2 or 3 arguments")
782
- }
783
- } else {
784
- // If no length is provided, default to 0
785
- c.tsw.WriteLiterally("0")
786
- }
787
-
788
- // Add type hint for proper zero value initialization
789
- typeHint := c.getTypeHintForSliceElement(goElemType)
790
- if typeHint != "" {
791
- if !hasCapacity {
792
- // If no capacity was provided, add undefined for capacity parameter
793
- c.tsw.WriteLiterally(", undefined")
794
- }
795
- c.tsw.WriteLiterally(", '")
796
- c.tsw.WriteLiterally(typeHint)
797
- c.tsw.WriteLiterally("'")
798
- }
799
-
800
- c.tsw.WriteLiterally(")")
801
- return nil // Handled make for selector expression slice type
802
- }
803
-
804
- // Handle selector expression channel types: make(pkg.ChannelType, bufferSize)
805
- if chanType, isChan := underlying.(*types.Chan); isChan {
806
- c.tsw.WriteLiterally("$.makeChannel<")
807
- c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
808
- c.tsw.WriteLiterally(">(")
809
-
810
- // If buffer size is provided, add it
811
- if len(exp.Args) >= 2 {
812
- if err := c.WriteValueExpr(exp.Args[1]); err != nil {
813
- return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
814
- }
815
- } else {
816
- // Default to 0 (unbuffered channel)
817
- c.tsw.WriteLiterally("0")
818
- }
819
-
820
- c.tsw.WriteLiterally(", ") // Add comma for zero value argument
821
-
822
- // Write the zero value for the channel's element type
823
- if chanType.Elem().String() == "struct{}" {
824
- c.tsw.WriteLiterally("{}")
825
- } else {
826
- c.WriteZeroValueForType(chanType.Elem())
827
- }
828
-
829
- // Add direction parameter
830
- c.tsw.WriteLiterally(", ")
831
-
832
- // Determine channel direction
833
- switch chanType.Dir() {
834
- case types.SendRecv:
835
- c.tsw.WriteLiterally("'both'")
836
- case types.SendOnly:
837
- c.tsw.WriteLiterally("'send'")
838
- case types.RecvOnly:
839
- c.tsw.WriteLiterally("'receive'")
840
- default:
841
- c.tsw.WriteLiterally("'both'") // Default to bidirectional
842
- }
843
-
844
- c.tsw.WriteLiterally(")")
845
- return nil // Handled make for selector expression channel type
846
- }
847
- }
848
- }
849
- // Fallthrough for unhandled make calls (e.g., channels)
850
- return errors.New("unhandled make call")
851
- case "string":
852
- // Handle string() conversion
853
- if len(exp.Args) == 1 {
854
- arg := exp.Args[0]
855
-
856
- // Case 1: Argument is a string literal string("...")
857
- if basicLit, isBasicLit := arg.(*ast.BasicLit); isBasicLit && basicLit.Kind == token.STRING {
858
- // Translate string("...") to "..." (no-op)
859
- c.WriteBasicLit(basicLit)
860
- return nil // Handled string literal conversion
861
- }
862
-
863
- // Case 2: Argument is a rune (int32) or a call to rune()
864
- innerCall, isCallExpr := arg.(*ast.CallExpr)
865
-
866
- if isCallExpr {
867
- // Check if it's a call to rune()
868
- if innerFunIdent, innerFunIsIdent := innerCall.Fun.(*ast.Ident); innerFunIsIdent && innerFunIdent.String() == "rune" {
869
- // Translate string(rune(val)) to $.runeOrStringToString(val)
870
- if len(innerCall.Args) == 1 {
871
- c.tsw.WriteLiterally("$.runeOrStringToString(")
872
- if err := c.WriteValueExpr(innerCall.Args[0]); err != nil {
873
- return fmt.Errorf("failed to write argument for string(rune) conversion: %w", err)
874
- }
875
- c.tsw.WriteLiterally(")")
876
- return nil // Handled string(rune)
877
- }
878
- }
879
- }
880
-
881
- // Handle direct string(int32) conversion
882
- // This assumes 'rune' is int32
883
- if tv, ok := c.pkg.TypesInfo.Types[arg]; ok {
884
- // Case 3a: Argument is already a string - no-op
885
- if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
886
- // Translate string(stringValue) to stringValue (no-op)
887
- if err := c.WriteValueExpr(arg); err != nil {
888
- return fmt.Errorf("failed to write argument for string(string) no-op conversion: %w", err)
889
- }
890
- return nil // Handled string(string) no-op
891
- }
892
-
893
- if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basic.Kind() == types.Int32 || basic.Kind() == types.UntypedRune) {
894
- // Translate string(rune_val) to $.runeOrStringToString(rune_val)
895
- c.tsw.WriteLiterally("$.runeOrStringToString(")
896
- if err := c.WriteValueExpr(arg); err != nil {
897
- return fmt.Errorf("failed to write argument for string(int32) conversion: %w", err)
898
- }
899
- c.tsw.WriteLiterally(")")
900
- return nil // Handled string(int32)
901
- }
902
-
903
- // Case 3: Argument is a slice of runes or bytes string([]rune{...}) or string([]byte{...})
904
- if sliceType, isSlice := tv.Type.Underlying().(*types.Slice); isSlice {
905
- if basic, isBasic := sliceType.Elem().Underlying().(*types.Basic); isBasic {
906
- // Handle string([]byte)
907
- if basic.Kind() == types.Uint8 {
908
- c.tsw.WriteLiterally("$.bytesToString(")
909
- if err := c.WriteValueExpr(arg); err != nil {
910
- return fmt.Errorf("failed to write argument for string([]byte) conversion: %w", err)
911
- }
912
- c.tsw.WriteLiterally(")")
913
- return nil // Handled string([]byte)
914
- }
915
- // Handle both runes (int32)
916
- if basic.Kind() == types.Int32 {
917
- // Translate string([]rune) to $.runesToString(...)
918
- c.tsw.WriteLiterally("$.runesToString(")
919
- if err := c.WriteValueExpr(arg); err != nil {
920
- return fmt.Errorf("failed to write argument for string([]rune) conversion: %w", err)
921
- }
922
- c.tsw.WriteLiterally(")")
923
- return nil // Handled string([]rune)
924
- }
925
- }
926
- }
927
-
928
- // Case 4: Argument is a generic type parameter (e.g., string | []byte)
929
- if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
930
- // Check if this is a []byte | string union constraint
931
- constraint := typeParam.Constraint()
932
- if constraint != nil {
933
- // For now, assume any type parameter that could be string or []byte needs the helper
934
- // This is a heuristic - in the future we could parse the constraint more precisely
935
- c.tsw.WriteLiterally("$.genericBytesOrStringToString(")
936
- if err := c.WriteValueExpr(arg); err != nil {
937
- return fmt.Errorf("failed to write argument for string(generic) conversion: %w", err)
938
- }
939
- c.tsw.WriteLiterally(")")
940
- return nil // Handled string(generic type parameter)
941
- }
942
- }
943
- }
944
- }
945
- // Return error for other unhandled string conversions
946
- return fmt.Errorf("unhandled string conversion: %s", exp.Fun)
947
- case "close":
948
- // Translate close(ch) to ch.close()
949
- if len(exp.Args) == 1 {
950
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
951
- return fmt.Errorf("failed to write channel in close call: %w", err)
952
- }
953
- c.tsw.WriteLiterally(".close()")
954
- return nil // Handled close
955
- }
956
- return errors.New("unhandled close call with incorrect number of arguments")
957
- case "append":
958
- // Translate append(slice, elements...) to $.append(slice, elements...)
959
- if len(exp.Args) >= 1 {
960
- c.tsw.WriteLiterally("$.append(")
961
- // The first argument is the slice
962
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
963
- return fmt.Errorf("failed to write slice in append call: %w", err)
964
- }
965
- // The remaining arguments are the elements to append
966
- for i, arg := range exp.Args[1:] {
967
- if i > 0 || len(exp.Args) > 1 { // Add comma before elements if there are any
968
- c.tsw.WriteLiterally(", ")
969
- }
970
-
971
- // Special case: append([]byte, string...) should convert string to bytes
972
- if exp.Ellipsis != token.NoPos && i == 0 { // This is the first element after slice and has ellipsis
973
- // Check if the slice is []byte and the argument is a string
974
- sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
975
- argType := c.pkg.TypesInfo.TypeOf(arg)
976
-
977
- if sliceType != nil && argType != nil {
978
- // Check if slice is []byte (Uint8Array)
979
- isSliceOfBytes := false
980
- if sliceUnder, ok := sliceType.Underlying().(*types.Slice); ok {
981
- if basicElem, ok := sliceUnder.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
982
- isSliceOfBytes = true
983
- }
984
- }
985
-
986
- // Check if argument is string (including untyped string)
987
- isArgString := false
988
- if basicArg, ok := argType.Underlying().(*types.Basic); ok && (basicArg.Kind() == types.String || basicArg.Kind() == types.UntypedString) {
989
- isArgString = true
990
- }
991
-
992
- if isSliceOfBytes && isArgString {
993
- // Convert string to bytes: append([]byte, string...) -> $.append(slice, ...$.stringToBytes(string))
994
- c.tsw.WriteLiterally("...$.stringToBytes(")
995
- if err := c.WriteValueExpr(arg); err != nil {
996
- return fmt.Errorf("failed to write string argument in append call: %w", err)
997
- }
998
- c.tsw.WriteLiterally(")")
999
- continue
1000
- }
1001
- }
1002
- }
1003
-
1004
- if err := c.WriteValueExpr(arg); err != nil {
1005
- return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
1006
- }
1007
- }
1008
- c.tsw.WriteLiterally(")")
1009
- return nil // Handled append
1010
- }
1011
- return errors.New("unhandled append call with incorrect number of arguments")
1012
- case "byte":
1013
- // Translate byte(arg) to $.byte(arg)
1014
- if len(exp.Args) != 1 {
1015
- return errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
1016
- }
1017
- c.tsw.WriteLiterally("$.byte(")
1018
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
1019
- return fmt.Errorf("failed to write argument for byte() conversion: %w", err)
1020
- }
1021
- c.tsw.WriteLiterally(")")
1022
- return nil // Handled byte() conversion
1023
- case "int":
1024
- // Handle int() conversion
1025
- if len(exp.Args) == 1 {
1026
- arg := exp.Args[0]
1027
-
1028
- // Check if we're converting FROM a type with receiver methods TO int
1029
- if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
1030
- if namedArgType, isNamed := argType.(*types.Named); isNamed {
1031
- argTypeName := namedArgType.Obj().Name()
1032
- // Check if the argument type has receiver methods
1033
- if c.hasReceiverMethods(argTypeName) {
1034
- // Check if we're converting to int (the underlying type)
1035
- if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
1036
- // This is a conversion from a type with methods to int
1037
- // Use valueOf() instead of $.int()
1038
- if err := c.WriteValueExpr(arg); err != nil {
1039
- return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
1040
- }
1041
- c.tsw.WriteLiterally(".valueOf()")
1042
- return nil // Handled conversion from type with methods to int
1043
- }
1044
- }
1045
- }
1046
- }
1047
-
1048
- // Default case: Translate int(value) to $.int(value)
1049
- c.tsw.WriteLiterally("$.int(")
1050
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
1051
- return fmt.Errorf("failed to write argument for int() conversion: %w", err)
1052
- }
1053
- c.tsw.WriteLiterally(")")
1054
- return nil // Handled int() conversion
1055
- }
1056
- // Return error for incorrect number of arguments
1057
- return fmt.Errorf("unhandled int conversion with incorrect number of arguments: %d != 1", len(exp.Args))
1058
- default:
1059
- // Check if this is a type conversion to a function type
1060
- if funIdent != nil {
1061
- if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil {
1062
- // Check if the object is a type name
1063
- if typeName, isType := obj.(*types.TypeName); isType {
1064
- // Make sure we have exactly one argument
1065
- if len(exp.Args) == 1 {
1066
- arg := exp.Args[0]
1067
-
1068
- // Check if we're converting FROM a type with receiver methods TO its underlying type
1069
- if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
1070
- if namedArgType, isNamed := argType.(*types.Named); isNamed {
1071
- argTypeName := namedArgType.Obj().Name()
1072
- // Check if the argument type has receiver methods
1073
- if c.hasReceiverMethods(argTypeName) {
1074
- // Check if we're converting to the underlying type
1075
- targetType := typeName.Type()
1076
- underlyingType := namedArgType.Underlying()
1077
- if types.Identical(targetType, underlyingType) {
1078
- // This is a conversion from a type with methods to its underlying type
1079
- // Use valueOf() instead of TypeScript cast
1080
- if err := c.WriteValueExpr(arg); err != nil {
1081
- return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
1082
- }
1083
- c.tsw.WriteLiterally(".valueOf()")
1084
- return nil // Handled conversion from type with methods to underlying type
1085
- }
1086
- }
1087
- }
1088
- }
1089
-
1090
- // Check if this is a function type
1091
- if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
1092
- // For function types, we need to add a __goTypeName property
1093
- c.tsw.WriteLiterally("Object.assign(")
67
+ return nil
68
+ }
1094
69
 
1095
- // Write the argument first
1096
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
1097
- return fmt.Errorf("failed to write argument for function type cast: %w", err)
1098
- }
70
+ // Check for type conversions
71
+ if handled, err := c.writeTypeConversion(exp, funIdent); handled {
72
+ return err
73
+ }
1099
74
 
1100
- // Add the __goTypeName property with the function type name
1101
- c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", funIdent.String())
1102
- return nil // Handled function type cast
1103
- } else {
1104
- // Check if this type has receiver methods
1105
- if c.hasReceiverMethods(funIdent.String()) {
1106
- // For types with methods, use class constructor
1107
- c.tsw.WriteLiterally("new ")
1108
- c.tsw.WriteLiterally(funIdent.String())
1109
- c.tsw.WriteLiterally("(")
1110
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
1111
- return fmt.Errorf("failed to write argument for type constructor: %w", err)
1112
- }
1113
- c.tsw.WriteLiterally(")")
1114
- return nil // Handled type with methods conversion
1115
- } else {
1116
- // For non-function types without methods, use the TypeScript "as" operator
1117
- c.tsw.WriteLiterally("(")
1118
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
1119
- return fmt.Errorf("failed to write argument for type cast: %w", err)
1120
- }
75
+ // Check if this is an async function call
76
+ _ = c.writeAsyncCall(exp, funIdent)
1121
77
 
1122
- // Then use the TypeScript "as" operator with the mapped type name
1123
- c.tsw.WriteLiterally(" as ")
1124
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
1125
- c.tsw.WriteLiterally(")")
1126
- return nil // Handled non-function type cast
1127
- }
1128
- }
1129
- }
1130
- }
1131
- }
1132
- }
78
+ // Not a special built-in, treat as a regular function call
79
+ if err := c.WriteValueExpr(expFun); err != nil {
80
+ return fmt.Errorf("failed to write function expression in call: %w", err)
81
+ }
1133
82
 
1134
- // Check if this is an async function call
1135
- if funIdent != nil {
1136
- // Get the object for this function identifier
1137
- if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
1138
- // This is an async function
1139
- c.tsw.WriteLiterally("await ")
1140
- }
1141
- }
83
+ c.addNonNullAssertion(expFun)
84
+ return c.writeCallArguments(exp)
85
+ }
1142
86
 
1143
- // Not a special built-in, treat as a regular function call
1144
- if err := c.WriteValueExpr(expFun); err != nil {
1145
- return fmt.Errorf("failed to write function expression in call: %w", err)
1146
- }
87
+ // Handle qualified type conversions like os.FileMode(value)
88
+ if selectorExpr, ok := expFun.(*ast.SelectorExpr); ok {
89
+ if handled, err := c.writeQualifiedTypeConversion(exp, selectorExpr); handled {
90
+ return err
91
+ }
92
+ }
1147
93
 
1148
- if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
1149
- if _, ok := funType.Underlying().(*types.Signature); ok {
1150
- // Check if this is a function parameter identifier that needs not-null assertion
1151
- if ident, isIdent := expFun.(*ast.Ident); isIdent {
1152
- // Check if this identifier is a function parameter
1153
- if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
1154
- if _, isVar := obj.(*types.Var); isVar {
1155
- // This is a variable (including function parameters)
1156
- // Function parameters that are function types need ! assertion
1157
- c.tsw.WriteLiterally("!")
1158
- }
1159
- }
1160
- } else if _, isNamed := funType.(*types.Named); isNamed {
1161
- c.tsw.WriteLiterally("!")
1162
- }
1163
- }
1164
- }
94
+ // Handle non-identifier function expressions (method calls, function literals, etc.)
95
+ // Check if this is an async method call (e.g., mu.Lock())
96
+ _ = c.writeAsyncMethodCall(exp)
1165
97
 
1166
- c.tsw.WriteLiterally("(")
1167
- for i, arg := range exp.Args {
1168
- if i != 0 {
1169
- c.tsw.WriteLiterally(", ")
1170
- }
1171
- // Check if this is the last argument and we have ellipsis (variadic call)
1172
- if exp.Ellipsis != token.NoPos && i == len(exp.Args)-1 {
1173
- c.tsw.WriteLiterally("...")
1174
- }
1175
- if err := c.WriteValueExpr(arg); err != nil {
1176
- return fmt.Errorf("failed to write argument %d in call: %w", i, err)
1177
- }
1178
- // Add non-null assertion for spread arguments that might be null
1179
- if exp.Ellipsis != token.NoPos && i == len(exp.Args)-1 {
1180
- // Check if the argument type is potentially nullable (slice)
1181
- if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
1182
- if _, isSlice := argType.Underlying().(*types.Slice); isSlice {
1183
- c.tsw.WriteLiterally("!")
1184
- }
1185
- }
1186
- }
1187
- }
1188
- c.tsw.WriteLiterally(")")
1189
- return nil // Handled regular function call
98
+ // If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
99
+ if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
100
+ c.tsw.WriteLiterally("(")
101
+ if err := c.WriteValueExpr(expFun); err != nil {
102
+ return fmt.Errorf("failed to write function literal in call: %w", err)
1190
103
  }
104
+ c.tsw.WriteLiterally(")")
1191
105
  } else {
1192
- // Check if this is an async method call (e.g., mu.Lock())
1193
- if selExpr, ok := expFun.(*ast.SelectorExpr); ok {
1194
- // Check if this is a method call on a variable (e.g., mu.Lock())
1195
- if ident, ok := selExpr.X.(*ast.Ident); ok {
1196
- // Get the type of the receiver
1197
- if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
1198
- if varObj, ok := obj.(*types.Var); ok {
1199
- // Get the type name and package
1200
- if namedType, ok := varObj.Type().(*types.Named); ok {
1201
- typeName := namedType.Obj().Name()
1202
- methodName := selExpr.Sel.Name
1203
-
1204
- // Check if the type is from an imported package
1205
- if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != c.pkg.Types {
1206
- // Use the actual package name from the type information
1207
- pkgName := typePkg.Name()
1208
-
1209
- // Check if this method is async based on metadata
1210
- if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
1211
- c.tsw.WriteLiterally("await ")
1212
- }
1213
- }
1214
- }
1215
- }
1216
- }
1217
- }
106
+ // Not an identifier (e.g., method call on a value, function call result)
107
+ if err := c.WriteValueExpr(expFun); err != nil {
108
+ return fmt.Errorf("failed to write method expression in call: %w", err)
1218
109
  }
1219
110
 
1220
- // If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
1221
- if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
1222
- c.tsw.WriteLiterally("(")
1223
- if err := c.WriteValueExpr(expFun); err != nil {
1224
- return fmt.Errorf("failed to write function literal in call: %w", err)
1225
- }
1226
- c.tsw.WriteLiterally(")")
111
+ // Check if this is a function call that returns a function (e.g., simpleIterator(m)())
112
+ // Add non-null assertion since the returned function could be null
113
+ if _, isCallExpr := expFun.(*ast.CallExpr); isCallExpr {
114
+ c.tsw.WriteLiterally("!")
1227
115
  } else {
1228
- // Not an identifier (e.g., method call on a value, function call result)
1229
- if err := c.WriteValueExpr(expFun); err != nil {
1230
- return fmt.Errorf("failed to write method expression in call: %w", err)
1231
- }
1232
-
1233
- // Check if this is a function call that returns a function (e.g., simpleIterator(m)())
1234
- // Add non-null assertion since the returned function could be null
1235
- if _, isCallExpr := expFun.(*ast.CallExpr); isCallExpr {
1236
- c.tsw.WriteLiterally("!")
1237
- } else if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
1238
- if _, ok := funType.Underlying().(*types.Signature); ok {
1239
- // Check if this is a function parameter identifier that needs not-null assertion
1240
- if ident, isIdent := expFun.(*ast.Ident); isIdent {
1241
- // Check if this identifier is a function parameter
1242
- if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
1243
- if _, isVar := obj.(*types.Var); isVar {
1244
- // This is a variable (including function parameters)
1245
- // Function parameters that are function types need ! assertion
1246
- c.tsw.WriteLiterally("!")
1247
- }
1248
- }
1249
- } else if _, isNamed := funType.(*types.Named); isNamed {
1250
- c.tsw.WriteLiterally("!")
1251
- }
1252
- } else {
1253
- // Check if the function type is nullable (e.g., func(...) | null)
1254
- // This handles cases where a function call returns a nullable function
1255
- funTypeStr := funType.String()
1256
- if strings.Contains(funTypeStr, "| null") || strings.Contains(funTypeStr, "null |") {
1257
- c.tsw.WriteLiterally("!")
1258
- }
1259
- }
1260
- }
116
+ c.addNonNullAssertion(expFun)
1261
117
  }
1262
118
  }
119
+
120
+ return c.writeCallArguments(exp)
121
+ }
122
+
123
+ // writeCallArguments writes the argument list for a function call
124
+ func (c *GoToTSCompiler) writeCallArguments(exp *ast.CallExpr) error {
1263
125
  c.tsw.WriteLiterally("(")
1264
126
  for i, arg := range exp.Args {
1265
127
  if i != 0 {
@@ -1285,111 +147,3 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
1285
147
  c.tsw.WriteLiterally(")")
1286
148
  return nil
1287
149
  }
1288
-
1289
- // hasSliceConstraint checks if an interface constraint includes slice types
1290
- // For constraints like ~[]E, this returns true
1291
- func hasSliceConstraint(iface *types.Interface) bool {
1292
- // Check if the interface has type terms that include slice types
1293
- for i := 0; i < iface.NumEmbeddeds(); i++ {
1294
- embedded := iface.EmbeddedType(i)
1295
- if union, ok := embedded.(*types.Union); ok {
1296
- for j := 0; j < union.Len(); j++ {
1297
- term := union.Term(j)
1298
- if _, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
1299
- return true
1300
- }
1301
- }
1302
- } else if _, isSlice := embedded.Underlying().(*types.Slice); isSlice {
1303
- return true
1304
- }
1305
- }
1306
- return false
1307
- }
1308
-
1309
- // getSliceElementTypeFromConstraint extracts the element type from a slice constraint
1310
- // For constraints like ~[]E, this returns E
1311
- func getSliceElementTypeFromConstraint(iface *types.Interface) types.Type {
1312
- // Check if the interface has type terms that include slice types
1313
- for i := 0; i < iface.NumEmbeddeds(); i++ {
1314
- embedded := iface.EmbeddedType(i)
1315
- if union, ok := embedded.(*types.Union); ok {
1316
- for j := 0; j < union.Len(); j++ {
1317
- term := union.Term(j)
1318
- if sliceType, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
1319
- return sliceType.Elem()
1320
- }
1321
- }
1322
- } else if sliceType, isSlice := embedded.Underlying().(*types.Slice); isSlice {
1323
- return sliceType.Elem()
1324
- }
1325
- }
1326
- return nil
1327
- }
1328
-
1329
- // hasMixedStringByteConstraint checks if an interface constraint includes both string and []byte types
1330
- // For constraints like string | []byte, this returns true
1331
- // For pure slice constraints like ~[]E, this returns false
1332
- func hasMixedStringByteConstraint(iface *types.Interface) bool {
1333
- hasString := false
1334
- hasByteSlice := false
1335
-
1336
- // Check if the interface has type terms that include both string and []byte
1337
- for i := 0; i < iface.NumEmbeddeds(); i++ {
1338
- embedded := iface.EmbeddedType(i)
1339
- if union, ok := embedded.(*types.Union); ok {
1340
- for j := 0; j < union.Len(); j++ {
1341
- term := union.Term(j)
1342
- termType := term.Type().Underlying()
1343
-
1344
- // Check for string type
1345
- if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
1346
- hasString = true
1347
- }
1348
-
1349
- // Check for []byte type
1350
- if sliceType, isSlice := termType.(*types.Slice); isSlice {
1351
- if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
1352
- hasByteSlice = true
1353
- }
1354
- }
1355
- }
1356
- } else {
1357
- // Handle non-union embedded types
1358
- termType := embedded.Underlying()
1359
-
1360
- // Check for string type
1361
- if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
1362
- hasString = true
1363
- }
1364
-
1365
- // Check for []byte type
1366
- if sliceType, isSlice := termType.(*types.Slice); isSlice {
1367
- if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
1368
- hasByteSlice = true
1369
- }
1370
- }
1371
- }
1372
- }
1373
-
1374
- // Return true only if we have both string and []byte in the constraint
1375
- return hasString && hasByteSlice
1376
- }
1377
-
1378
- // getTypeHintForSliceElement returns the appropriate type hint for makeSlice based on the Go element type
1379
- func (c *GoToTSCompiler) getTypeHintForSliceElement(elemType types.Type) string {
1380
- if basicType, isBasic := elemType.(*types.Basic); isBasic {
1381
- switch basicType.Kind() {
1382
- case types.Int, types.Int8, types.Int16, types.Int32, types.Int64,
1383
- types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64,
1384
- types.Float32, types.Float64, types.Complex64, types.Complex128:
1385
- return "number"
1386
- case types.Bool:
1387
- return "boolean"
1388
- case types.String:
1389
- return "string"
1390
- }
1391
- }
1392
- // For other types (structs, interfaces, pointers, etc.), don't provide a hint
1393
- // This will use the default null initialization which is appropriate for object types
1394
- return ""
1395
- }