goscript 0.0.57 → 0.0.59

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 (44) hide show
  1. package/README.md +40 -33
  2. package/compiler/analysis.go +115 -19
  3. package/compiler/assignment.go +163 -217
  4. package/compiler/compiler.go +35 -31
  5. package/compiler/composite-lit.go +233 -196
  6. package/compiler/constraint.go +88 -0
  7. package/compiler/decl.go +418 -7
  8. package/compiler/expr-call-async.go +20 -34
  9. package/compiler/expr-call-builtins.go +19 -0
  10. package/compiler/expr-call-helpers.go +0 -28
  11. package/compiler/expr-call-make.go +93 -343
  12. package/compiler/expr-call-type-conversion.go +221 -249
  13. package/compiler/expr-call.go +70 -69
  14. package/compiler/expr-selector.go +21 -24
  15. package/compiler/expr.go +3 -60
  16. package/compiler/protobuf.go +180 -36
  17. package/compiler/spec-value.go +132 -24
  18. package/compiler/spec.go +14 -55
  19. package/compiler/stmt-assign.go +338 -356
  20. package/compiler/stmt-range.go +4 -24
  21. package/compiler/stmt.go +99 -172
  22. package/compiler/type-utils.go +185 -0
  23. package/compiler/type.go +26 -80
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +3 -0
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/builtin/type.js +8 -2
  28. package/dist/gs/builtin/type.js.map +1 -1
  29. package/dist/gs/fmt/fmt.js +113 -16
  30. package/dist/gs/fmt/fmt.js.map +1 -1
  31. package/dist/gs/runtime/runtime.d.ts +1 -1
  32. package/dist/gs/runtime/runtime.js +1 -1
  33. package/dist/gs/slices/slices.d.ts +23 -0
  34. package/dist/gs/slices/slices.js +61 -0
  35. package/dist/gs/slices/slices.js.map +1 -1
  36. package/go.mod +8 -8
  37. package/go.sum +14 -14
  38. package/gs/builtin/slice.ts +5 -2
  39. package/gs/builtin/type.ts +13 -6
  40. package/gs/fmt/fmt.test.ts +176 -0
  41. package/gs/fmt/fmt.ts +109 -18
  42. package/gs/runtime/runtime.ts +1 -1
  43. package/gs/slices/slices.ts +68 -0
  44. package/package.json +3 -3
@@ -43,7 +43,7 @@ func (c *GoToTSCompiler) writeArrayTypeConversion(exp *ast.CallExpr) (handled bo
43
43
  if len(exp.Args) == 1 {
44
44
  arg := exp.Args[0]
45
45
  if tv, ok := c.pkg.TypesInfo.Types[arg]; ok && tv.Type != nil {
46
- if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
46
+ if c.isStringType(tv.Type) {
47
47
  // Translate []rune(stringValue) to $.stringToRunes(stringValue)
48
48
  c.tsw.WriteLiterally("$.stringToRunes(")
49
49
  if err := c.WriteValueExpr(arg); err != nil {
@@ -81,7 +81,7 @@ func (c *GoToTSCompiler) writeArrayTypeConversion(exp *ast.CallExpr) (handled bo
81
81
  // Check if the argument is a named type with a slice underlying type
82
82
  if namedArgType, isNamed := argType.(*types.Named); isNamed {
83
83
  // Check if the named type has receiver methods (is a wrapper type)
84
- if c.analysis.IsNamedBasicType(namedArgType) {
84
+ if c.isWrapperType(namedArgType) {
85
85
  // Check if the underlying type matches the target slice type
86
86
  if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
87
87
  // Get the target slice type
@@ -160,6 +160,16 @@ func (c *GoToTSCompiler) writeStringConversion(exp *ast.CallExpr) error {
160
160
  return nil
161
161
  }
162
162
 
163
+ if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.Uint8 {
164
+ // Translate string(byte_val) to $.runeOrStringToString(byte_val)
165
+ c.tsw.WriteLiterally("$.runeOrStringToString(")
166
+ if err := c.WriteValueExpr(arg); err != nil {
167
+ return fmt.Errorf("failed to write argument for string(byte) conversion: %w", err)
168
+ }
169
+ c.tsw.WriteLiterally(")")
170
+ return nil
171
+ }
172
+
163
173
  // Case 3: Argument is a slice of runes or bytes string([]rune{...}) or string([]byte{...})
164
174
  if c.isByteSliceType(tv.Type) {
165
175
  c.tsw.WriteLiterally("$.bytesToString(")
@@ -180,14 +190,41 @@ func (c *GoToTSCompiler) writeStringConversion(exp *ast.CallExpr) error {
180
190
 
181
191
  // Case 4: Argument is a generic type parameter (e.g., string | []byte)
182
192
  if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
183
- // Check if this is a []byte | string union constraint
193
+ // Check if this is a []byte | string union constraint precisely
184
194
  constraint := typeParam.Constraint()
185
195
  if constraint != nil {
186
- // For now, assume any type parameter that could be string or []byte needs the helper
187
- // This is a heuristic - in the future we could parse the constraint more precisely
196
+ hasString := constraintIncludesString(constraint)
197
+ hasBytes := constraintIncludesByteSlice(constraint)
198
+
199
+ if hasString && hasBytes {
200
+ c.tsw.WriteLiterally("$.genericBytesOrStringToString(")
201
+ if err := c.WriteValueExpr(arg); err != nil {
202
+ return fmt.Errorf("failed to write argument for string(generic union) conversion: %w", err)
203
+ }
204
+ c.tsw.WriteLiterally(")")
205
+ return nil
206
+ }
207
+ if hasString {
208
+ // string(T) where T is constrained to string: no-op
209
+ if err := c.WriteValueExpr(arg); err != nil {
210
+ return fmt.Errorf("failed to write argument for string(string-constrained) conversion: %w", err)
211
+ }
212
+ return nil
213
+ }
214
+ if hasBytes {
215
+ // string(T) where T is constrained to []byte
216
+ c.tsw.WriteLiterally("$.bytesToString(")
217
+ if err := c.WriteValueExpr(arg); err != nil {
218
+ return fmt.Errorf("failed to write argument for string([]byte-constrained) conversion: %w", err)
219
+ }
220
+ c.tsw.WriteLiterally(")")
221
+ return nil
222
+ }
223
+
224
+ // Fallback to previous behavior if we cannot determine precisely
188
225
  c.tsw.WriteLiterally("$.genericBytesOrStringToString(")
189
226
  if err := c.WriteValueExpr(arg); err != nil {
190
- return fmt.Errorf("failed to write argument for string(generic) conversion: %w", err)
227
+ return fmt.Errorf("failed to write argument for string(generic fallback) conversion: %w", err)
191
228
  }
192
229
  c.tsw.WriteLiterally(")")
193
230
  return nil
@@ -198,161 +235,173 @@ func (c *GoToTSCompiler) writeStringConversion(exp *ast.CallExpr) error {
198
235
  return fmt.Errorf("unhandled string conversion: %s", exp.Fun)
199
236
  }
200
237
 
201
- // writeTypeConversion handles named type conversions
202
- func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Ident) (handled bool, err error) {
203
- // Check if this is a type conversion to a function type
204
- if funIdent == nil {
205
- return false, nil
238
+ // handleTypeConversionCommon contains the shared logic for type conversions
239
+ func (c *GoToTSCompiler) handleTypeConversionCommon(
240
+ typeName *types.TypeName,
241
+ arg ast.Expr,
242
+ typeNameStr string,
243
+ writeTypeNameFunc func() error,
244
+ ) error {
245
+ // Check if we're converting FROM a type with receiver methods TO its underlying type
246
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
247
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
248
+ // Check if the argument type is a wrapper type
249
+ if c.isWrapperType(namedArgType) {
250
+ // Check if we're converting to the underlying type
251
+ targetType := typeName.Type()
252
+ underlyingType := namedArgType.Underlying()
253
+ if types.Identical(targetType, underlyingType) {
254
+ // This is a conversion from a wrapper type to its underlying type
255
+ // Since wrapper types are now type aliases, just write the value directly
256
+ return c.WriteValueExpr(arg)
257
+ }
258
+ }
259
+ }
206
260
  }
207
261
 
208
- if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil {
209
- // Check if the object is a type name
210
- if typeName, isType := obj.(*types.TypeName); isType {
211
- // Make sure we have exactly one argument
212
- if len(exp.Args) == 1 {
213
- arg := exp.Args[0]
214
-
215
- // Check if we're converting FROM a type with receiver methods TO its underlying type
216
- if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
217
- if namedArgType, isNamed := argType.(*types.Named); isNamed {
218
- // Check if the argument type is a wrapper type
219
- if c.analysis.IsNamedBasicType(namedArgType) {
220
- // Check if we're converting to the underlying type
221
- targetType := typeName.Type()
222
- underlyingType := namedArgType.Underlying()
223
- if types.Identical(targetType, underlyingType) {
224
- // This is a conversion from a wrapper type to its underlying type
225
- // Since wrapper types are now type aliases, just write the value directly
226
- if err := c.WriteValueExpr(arg); err != nil {
227
- return true, fmt.Errorf("failed to write argument for type conversion: %w", err)
228
- }
229
- return true, nil
230
- }
231
- }
262
+ // Check if this is a function type
263
+ if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
264
+ // For function types, we need to add a __goTypeName property
265
+ c.tsw.WriteLiterally("Object.assign(")
266
+
267
+ // Write the argument first
268
+ if err := c.WriteValueExpr(arg); err != nil {
269
+ return fmt.Errorf("failed to write argument for function type cast: %w", err)
270
+ }
271
+
272
+ // Add the __goTypeName property with the function type name
273
+ c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", typeNameStr)
274
+ return nil
275
+ }
276
+
277
+ // Check if this is a wrapper type
278
+ if c.isWrapperType(typeName.Type()) {
279
+ // For wrapper types, use type casting instead of constructor calls
280
+ c.tsw.WriteLiterally("(")
281
+ if err := c.WriteValueExpr(arg); err != nil {
282
+ return fmt.Errorf("failed to write argument for wrapper type cast: %w", err)
283
+ }
284
+ c.tsw.WriteLiterally(" as ")
285
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
286
+ c.tsw.WriteLiterally(")")
287
+ return nil
288
+ }
289
+
290
+ // Check if this is a simple named type (no methods, not struct, not interface)
291
+ if namedType, ok := typeName.Type().(*types.Named); ok {
292
+ // Don't use constructors for simple named types without methods
293
+ if namedType.NumMethods() == 0 {
294
+ // Exclude struct and interface types - they should still use constructors
295
+ if _, isStruct := namedType.Underlying().(*types.Struct); !isStruct {
296
+ if _, isInterface := namedType.Underlying().(*types.Interface); !isInterface {
297
+ // Simple named type without methods - use type casting
298
+ c.tsw.WriteLiterally("(")
299
+ if err := c.WriteValueExpr(arg); err != nil {
300
+ return fmt.Errorf("failed to write argument for simple named type cast: %w", err)
232
301
  }
302
+ c.tsw.WriteLiterally(" as ")
303
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
304
+ c.tsw.WriteLiterally(")")
305
+ return nil
233
306
  }
307
+ }
308
+ }
309
+ }
234
310
 
235
- // Check if this is a function type
236
- if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
237
- // For function types, we need to add a __goTypeName property
238
- c.tsw.WriteLiterally("Object.assign(")
311
+ // Check if this type has receiver methods
312
+ if c.hasReceiverMethods(typeNameStr) {
313
+ // For types with methods that are NOT wrapper types, still use class constructor
314
+ c.tsw.WriteLiterally("new ")
315
+ if err := writeTypeNameFunc(); err != nil {
316
+ return fmt.Errorf("failed to write type name: %w", err)
317
+ }
318
+ c.tsw.WriteLiterally("(")
319
+
320
+ // Use auto-wrapping for the constructor argument (only if writeTypeNameFunc writes a simple identifier)
321
+ // The constructor parameter type is the underlying type of the named type
322
+ // For MyMode (which is type MyMode os.FileMode), the constructor expects os.FileMode
323
+ constructorParamType := typeName.Type()
324
+ if namedType, ok := typeName.Type().(*types.Named); ok {
325
+ // For named types, the constructor expects the underlying type
326
+ constructorParamType = namedType.Underlying()
327
+ }
239
328
 
240
- // Write the argument first
241
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
242
- return true, fmt.Errorf("failed to write argument for function type cast: %w", err)
243
- }
329
+ if err := c.writeAutoWrappedArgument(arg, constructorParamType); err != nil {
330
+ return fmt.Errorf("failed to write argument for type constructor: %w", err)
331
+ }
332
+ c.tsw.WriteLiterally(")")
333
+ return nil
334
+ }
244
335
 
245
- // Add the __goTypeName property with the function type name
246
- c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", funIdent.String())
247
- return true, nil
248
- } else {
249
- // Check if this is a wrapper type
250
- isWrapperType := c.analysis.IsNamedBasicType(typeName.Type())
251
- if isWrapperType {
252
- // For wrapper types, use type casting instead of constructor calls
253
- c.tsw.WriteLiterally("(")
254
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
255
- return true, fmt.Errorf("failed to write argument for wrapper type cast: %w", err)
256
- }
257
- c.tsw.WriteLiterally(" as ")
258
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
259
- c.tsw.WriteLiterally(")")
260
- return true, nil
261
- }
336
+ // Determine if this type should use constructor syntax
337
+ shouldUseConstructor := false
262
338
 
263
- // Check if this is a simple named type (no methods, not struct, not interface)
264
- if namedType, ok := typeName.Type().(*types.Named); ok {
265
- // Don't use constructors for simple named types without methods
266
- if namedType.NumMethods() == 0 {
267
- // Exclude struct and interface types - they should still use constructors
268
- if _, isStruct := namedType.Underlying().(*types.Struct); !isStruct {
269
- if _, isInterface := namedType.Underlying().(*types.Interface); !isInterface {
270
- // Simple named type without methods - use type casting
271
- c.tsw.WriteLiterally("(")
272
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
273
- return true, fmt.Errorf("failed to write argument for simple named type cast: %w", err)
274
- }
275
- c.tsw.WriteLiterally(" as ")
276
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
277
- c.tsw.WriteLiterally(")")
278
- return true, nil
279
- }
280
- }
281
- }
282
- }
339
+ // Check if it's a type alias (like os.FileMode)
340
+ if alias, isAlias := typeName.Type().(*types.Alias); isAlias {
341
+ // For type aliases, check the underlying type
342
+ if _, isInterface := alias.Underlying().(*types.Interface); !isInterface {
343
+ if _, isStruct := alias.Underlying().(*types.Struct); !isStruct {
344
+ // For non-struct, non-interface type aliases, use constructor
345
+ shouldUseConstructor = true
346
+ }
347
+ }
348
+ } else if namedType, isNamed := typeName.Type().(*types.Named); isNamed {
349
+ // For named types, check if they have receiver methods in the current package
350
+ // or if they follow the pattern of non-struct, non-interface named types
351
+ if c.hasReceiverMethods(typeNameStr) {
352
+ shouldUseConstructor = true
353
+ } else if _, isInterface := namedType.Underlying().(*types.Interface); !isInterface {
354
+ if _, isStruct := namedType.Underlying().(*types.Struct); !isStruct {
355
+ // For non-struct, non-interface named types, use constructor
356
+ shouldUseConstructor = true
357
+ }
358
+ }
359
+ }
283
360
 
284
- // Check if this type has receiver methods
285
- if c.hasReceiverMethods(funIdent.String()) {
286
- // For types with methods that are NOT wrapper types, still use class constructor
287
- c.tsw.WriteLiterally("new ")
288
- c.tsw.WriteLiterally(funIdent.String())
289
- c.tsw.WriteLiterally("(")
290
-
291
- // Use auto-wrapping for the constructor argument
292
- // The constructor parameter type is the underlying type of the named type
293
- // For MyMode (which is type MyMode os.FileMode), the constructor expects os.FileMode
294
- constructorParamType := typeName.Type()
295
- if namedType, ok := typeName.Type().(*types.Named); ok {
296
- // For named types, the constructor expects the underlying type
297
- constructorParamType = namedType.Underlying()
298
- }
361
+ if shouldUseConstructor {
362
+ // For types that should use constructors, use class constructor
363
+ c.tsw.WriteLiterally("new ")
364
+ if err := writeTypeNameFunc(); err != nil {
365
+ return fmt.Errorf("failed to write type name: %w", err)
366
+ }
367
+ c.tsw.WriteLiterally("(")
368
+ if err := c.WriteValueExpr(arg); err != nil {
369
+ return fmt.Errorf("failed to write argument for type constructor: %w", err)
370
+ }
371
+ c.tsw.WriteLiterally(")")
372
+ return nil
373
+ }
299
374
 
300
- if err := c.writeAutoWrappedArgument(exp.Args[0], constructorParamType); err != nil {
301
- return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
302
- }
303
- c.tsw.WriteLiterally(")")
304
- return true, nil
305
- } else {
306
- // Determine if this type should use constructor syntax
307
- shouldUseConstructor := false
308
-
309
- // Check if it's a type alias (like os.FileMode)
310
- if alias, isAlias := typeName.Type().(*types.Alias); isAlias {
311
- // For type aliases, check the underlying type
312
- if _, isInterface := alias.Underlying().(*types.Interface); !isInterface {
313
- if _, isStruct := alias.Underlying().(*types.Struct); !isStruct {
314
- // For non-struct, non-interface type aliases, use constructor
315
- shouldUseConstructor = true
316
- }
317
- }
318
- } else if namedType, isNamed := typeName.Type().(*types.Named); isNamed {
319
- // For named types, check if they have receiver methods in the current package
320
- // or if they follow the pattern of non-struct, non-interface named types
321
- if c.hasReceiverMethods(funIdent.String()) {
322
- shouldUseConstructor = true
323
- } else if _, isInterface := namedType.Underlying().(*types.Interface); !isInterface {
324
- if _, isStruct := namedType.Underlying().(*types.Struct); !isStruct {
325
- // For non-struct, non-interface named types, use constructor
326
- shouldUseConstructor = true
327
- }
328
- }
329
- }
375
+ // For types that don't need constructors, use the TypeScript "as" operator
376
+ c.tsw.WriteLiterally("(")
377
+ if err := c.WriteValueExpr(arg); err != nil {
378
+ return fmt.Errorf("failed to write argument for type cast: %w", err)
379
+ }
330
380
 
331
- if shouldUseConstructor {
332
- // For types that should use constructors, use class constructor
333
- c.tsw.WriteLiterally("new ")
334
- c.tsw.WriteLiterally(funIdent.String())
335
- c.tsw.WriteLiterally("(")
336
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
337
- return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
338
- }
339
- c.tsw.WriteLiterally(")")
340
- return true, nil
341
- } else {
342
- // For types that don't need constructors, use the TypeScript "as" operator
343
- c.tsw.WriteLiterally("(")
344
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
345
- return true, fmt.Errorf("failed to write argument for type cast: %w", err)
346
- }
381
+ // Then use the TypeScript "as" operator with the mapped type name
382
+ c.tsw.WriteLiterally(" as ")
383
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
384
+ c.tsw.WriteLiterally(")")
385
+ return nil
386
+ }
347
387
 
348
- // Then use the TypeScript "as" operator with the mapped type name
349
- c.tsw.WriteLiterally(" as ")
350
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
351
- c.tsw.WriteLiterally(")")
352
- return true, nil
353
- }
354
- }
355
- }
388
+ // writeTypeConversion handles named type conversions
389
+ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Ident) (handled bool, err error) {
390
+ // Check if this is a type conversion to a function type
391
+ if funIdent == nil {
392
+ return false, nil
393
+ }
394
+
395
+ if obj := c.objectOfIdent(funIdent); obj != nil {
396
+ // Check if the object is a type name
397
+ if typeName, isType := obj.(*types.TypeName); isType {
398
+ // Make sure we have exactly one argument
399
+ if len(exp.Args) == 1 {
400
+ err := c.handleTypeConversionCommon(typeName, exp.Args[0], funIdent.String(), func() error {
401
+ c.tsw.WriteLiterally(funIdent.String())
402
+ return nil
403
+ })
404
+ return true, err
356
405
  }
357
406
  }
358
407
  }
@@ -372,7 +421,7 @@ func (c *GoToTSCompiler) writeIntConversion(exp *ast.CallExpr) error {
372
421
  if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
373
422
  if namedArgType, isNamed := argType.(*types.Named); isNamed {
374
423
  // Check if the argument type is a wrapper type
375
- if c.analysis.IsNamedBasicType(namedArgType) {
424
+ if c.isWrapperType(namedArgType) {
376
425
  // Check if we're converting to int (the underlying type)
377
426
  if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
378
427
  // This is a conversion from a wrapper type to int
@@ -398,112 +447,35 @@ func (c *GoToTSCompiler) writeIntConversion(exp *ast.CallExpr) error {
398
447
  // writeQualifiedTypeConversion handles qualified type conversions like os.FileMode(value)
399
448
  func (c *GoToTSCompiler) writeQualifiedTypeConversion(exp *ast.CallExpr, selectorExpr *ast.SelectorExpr) (handled bool, err error) {
400
449
  // Check if this is a type conversion by looking up the selector in the type info
401
- if obj := c.pkg.TypesInfo.Uses[selectorExpr.Sel]; obj != nil {
450
+ if obj := c.objectOfIdent(selectorExpr.Sel); obj != nil {
402
451
  // Check if the object is a type name
403
452
  if typeName, isType := obj.(*types.TypeName); isType {
404
453
  // Make sure we have exactly one argument
405
454
  if len(exp.Args) == 1 {
406
- arg := exp.Args[0]
407
-
408
- // Check if we're converting FROM a type with receiver methods TO its underlying type
409
- if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
410
- if namedArgType, isNamed := argType.(*types.Named); isNamed {
411
- // Check if the argument type is a wrapper type
412
- if c.analysis.IsNamedBasicType(namedArgType) {
413
- // Check if we're converting to the underlying type
414
- targetType := typeName.Type()
415
- underlyingType := namedArgType.Underlying()
416
- if types.Identical(targetType, underlyingType) {
417
- // This is a conversion from a wrapper type to its underlying type
418
- // Since wrapper types are now type aliases, just write the value directly
419
- if err := c.WriteValueExpr(arg); err != nil {
420
- return true, fmt.Errorf("failed to write argument for type conversion: %w", err)
421
- }
422
- return true, nil
423
- }
424
- }
425
- }
426
- }
427
-
428
- // Check if this is a function type
429
- if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
430
- // For function types, we need to add a __goTypeName property
431
- c.tsw.WriteLiterally("Object.assign(")
432
-
433
- // Write the argument first
434
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
435
- return true, fmt.Errorf("failed to write argument for function type cast: %w", err)
436
- }
437
-
438
- // Add the __goTypeName property with the function type name
439
- c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", selectorExpr.Sel.Name)
440
- return true, nil
441
- } else {
442
- // Check if this is a wrapper type
443
- isWrapperType := c.analysis.IsNamedBasicType(typeName.Type())
444
- if isWrapperType {
445
- // For wrapper types, use type casting instead of constructor calls
446
- c.tsw.WriteLiterally("(")
447
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
448
- return true, fmt.Errorf("failed to write argument for wrapper type cast: %w", err)
449
- }
450
- c.tsw.WriteLiterally(" as ")
451
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
452
- c.tsw.WriteLiterally(")")
453
- return true, nil
454
- }
455
-
456
- // Check if this is a simple named type (no methods, not struct, not interface)
457
- if namedType, ok := typeName.Type().(*types.Named); ok {
458
- // Don't use constructors for simple named types without methods
459
- if namedType.NumMethods() == 0 {
460
- // Exclude struct and interface types - they should still use constructors
461
- if _, isStruct := namedType.Underlying().(*types.Struct); !isStruct {
462
- if _, isInterface := namedType.Underlying().(*types.Interface); !isInterface {
463
- // Simple named type without methods - use type casting
464
- c.tsw.WriteLiterally("(")
465
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
466
- return true, fmt.Errorf("failed to write argument for simple named type cast: %w", err)
467
- }
468
- c.tsw.WriteLiterally(" as ")
469
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
470
- c.tsw.WriteLiterally(")")
471
- return true, nil
472
- }
473
- }
474
- }
475
- }
476
-
477
- // Check if this type has receiver methods
478
- if c.hasReceiverMethods(selectorExpr.Sel.Name) {
479
- // For types with methods that are NOT wrapper types, still use class constructor
480
- c.tsw.WriteLiterally("new ")
481
- if err := c.WriteSelectorExpr(selectorExpr); err != nil {
482
- return true, fmt.Errorf("failed to write qualified type name: %w", err)
483
- }
484
- c.tsw.WriteLiterally("(")
485
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
486
- return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
487
- }
488
- c.tsw.WriteLiterally(")")
489
- return true, nil
490
- } else {
491
- // For types that don't need constructors, use the TypeScript "as" operator
492
- c.tsw.WriteLiterally("(")
493
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
494
- return true, fmt.Errorf("failed to write argument for type cast: %w", err)
495
- }
496
-
497
- // Then use the TypeScript "as" operator with the mapped type name
498
- c.tsw.WriteLiterally(" as ")
499
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
500
- c.tsw.WriteLiterally(")")
501
- return true, nil
502
- }
503
- }
455
+ err := c.handleTypeConversionCommon(typeName, exp.Args[0], selectorExpr.Sel.Name, func() error {
456
+ return c.WriteSelectorExpr(selectorExpr)
457
+ })
458
+ return true, err
504
459
  }
505
460
  }
506
461
  }
507
462
 
508
463
  return false, nil
509
464
  }
465
+
466
+ // Helper: detect if a constraint includes string or []byte
467
+ func constraintIncludesString(t types.Type) bool {
468
+ iface, ok := t.Underlying().(*types.Interface)
469
+ if !ok {
470
+ return false
471
+ }
472
+ return analyzeConstraint(iface).HasString
473
+ }
474
+
475
+ func constraintIncludesByteSlice(t types.Type) bool {
476
+ iface, ok := t.Underlying().(*types.Interface)
477
+ if !ok {
478
+ return false
479
+ }
480
+ return analyzeConstraint(iface).HasByteSlice
481
+ }