goscript 0.0.61 → 0.0.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -46
- package/compiler/analysis.go +621 -19
- package/compiler/analysis_test.go +3 -3
- package/compiler/assignment.go +100 -0
- package/compiler/builtin_test.go +1 -1
- package/compiler/compiler.go +76 -16
- package/compiler/compiler_test.go +9 -9
- package/compiler/composite-lit.go +29 -8
- package/compiler/decl.go +20 -11
- package/compiler/expr-call-async.go +26 -1
- package/compiler/expr-call-builtins.go +60 -4
- package/compiler/expr-call-type-conversion.go +37 -5
- package/compiler/expr-call.go +26 -6
- package/compiler/expr-selector.go +35 -2
- package/compiler/expr-type.go +12 -2
- package/compiler/expr.go +61 -0
- package/compiler/index.test.ts +3 -1
- package/compiler/lit.go +13 -4
- package/compiler/spec-struct.go +30 -8
- package/compiler/spec-value.go +2 -2
- package/compiler/spec.go +23 -4
- package/compiler/stmt-assign.go +124 -0
- package/compiler/stmt-range.go +2 -2
- package/compiler/stmt.go +160 -14
- package/compiler/type-info.go +3 -5
- package/compiler/type-utils.go +40 -1
- package/compiler/type.go +52 -14
- package/dist/gs/builtin/builtin.d.ts +8 -1
- package/dist/gs/builtin/builtin.js +26 -1
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/errors.d.ts +1 -0
- package/dist/gs/builtin/errors.js +8 -0
- package/dist/gs/builtin/errors.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +5 -4
- package/dist/gs/builtin/slice.js +88 -51
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +23 -2
- package/dist/gs/builtin/type.js +125 -0
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +3 -0
- package/dist/gs/builtin/varRef.js +6 -1
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/reader.gs.d.ts +1 -1
- package/dist/gs/bytes/reader.gs.js +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +2 -2
- package/dist/gs/reflect/index.js +1 -1
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.d.ts +3 -2
- package/dist/gs/reflect/map.js +37 -3
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +53 -12
- package/dist/gs/reflect/type.js +906 -31
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +11 -12
- package/dist/gs/reflect/types.js +26 -15
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.d.ts +4 -4
- package/dist/gs/reflect/value.js +8 -2
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +21 -0
- package/dist/gs/slices/slices.js +48 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js +20 -2
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.d.ts +3 -3
- package/dist/gs/sync/atomic/type.gs.js +13 -7
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/unicode/utf8/utf8.d.ts +2 -2
- package/dist/gs/unicode/utf8/utf8.js +10 -6
- package/dist/gs/unicode/utf8/utf8.js.map +1 -1
- package/go.mod +6 -6
- package/go.sum +12 -8
- package/gs/builtin/builtin.ts +27 -2
- package/gs/builtin/errors.ts +12 -0
- package/gs/builtin/slice.ts +126 -55
- package/gs/builtin/type.ts +159 -2
- package/gs/builtin/varRef.ts +8 -2
- package/gs/bytes/reader.gs.ts +2 -2
- package/gs/math/hypot.gs.test.ts +3 -1
- package/gs/math/pow10.gs.test.ts +5 -4
- package/gs/reflect/index.ts +3 -2
- package/gs/reflect/map.test.ts +7 -6
- package/gs/reflect/map.ts +49 -7
- package/gs/reflect/type.ts +1150 -57
- package/gs/reflect/types.ts +34 -21
- package/gs/reflect/value.ts +12 -6
- package/gs/slices/slices.ts +55 -0
- package/gs/strconv/atoi.gs.ts +18 -2
- package/gs/sync/atomic/type.gs.ts +15 -10
- package/gs/unicode/utf8/utf8.ts +12 -8
- package/package.json +23 -14
package/compiler/stmt-assign.go
CHANGED
|
@@ -168,6 +168,21 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
|
|
|
168
168
|
|
|
169
169
|
// Handle single assignment using writeAssignmentCore
|
|
170
170
|
if len(exp.Lhs) == 1 {
|
|
171
|
+
// Check for type shadowing (e.g., field := field{...})
|
|
172
|
+
// In this case, we need to rename the variable to avoid TypeScript shadowing
|
|
173
|
+
if nodeInfo := c.analysis.NodeData[exp]; nodeInfo != nil && nodeInfo.ShadowingInfo != nil {
|
|
174
|
+
if lhsIdent, ok := exp.Lhs[0].(*ast.Ident); ok && lhsIdent.Name != "_" {
|
|
175
|
+
if renamedVar, hasTypeShadow := nodeInfo.ShadowingInfo.TypeShadowedVars[lhsIdent.Name]; hasTypeShadow {
|
|
176
|
+
if err := c.writeTypeShadowedAssignment(exp, lhsIdent.Name, renamedVar); err != nil {
|
|
177
|
+
return err
|
|
178
|
+
}
|
|
179
|
+
c.writeInlineComment(exp)
|
|
180
|
+
c.tsw.WriteLine("")
|
|
181
|
+
return nil
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
171
186
|
addDeclaration := exp.Tok == token.DEFINE
|
|
172
187
|
if err := c.writeAssignmentCore(exp.Lhs, exp.Rhs, exp.Tok, addDeclaration); err != nil {
|
|
173
188
|
return err
|
|
@@ -305,6 +320,59 @@ func (c *GoToTSCompiler) writeMultiVarAssignFromCall(lhs []ast.Expr, callExpr *a
|
|
|
305
320
|
}
|
|
306
321
|
|
|
307
322
|
if allNewVars && anyNewVars {
|
|
323
|
+
// Check if any variable needs VarRef - if so, we need a different approach
|
|
324
|
+
anyNeedsVarRef := false
|
|
325
|
+
needsVarRefVars := make([]bool, len(lhs))
|
|
326
|
+
for i, lhsExpr := range lhs {
|
|
327
|
+
if ident, ok := lhsExpr.(*ast.Ident); ok && ident.Name != "_" {
|
|
328
|
+
if obj := c.pkg.TypesInfo.Defs[ident]; obj != nil {
|
|
329
|
+
if c.analysis.NeedsVarRef(obj) {
|
|
330
|
+
needsVarRefVars[i] = true
|
|
331
|
+
anyNeedsVarRef = true
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if anyNeedsVarRef {
|
|
338
|
+
// Use temp variables for destructuring, then wrap in VarRef as needed
|
|
339
|
+
c.tsw.WriteLiterally("let [")
|
|
340
|
+
for i, lhsExpr := range lhs {
|
|
341
|
+
if i != 0 {
|
|
342
|
+
c.tsw.WriteLiterally(", ")
|
|
343
|
+
}
|
|
344
|
+
if ident, ok := lhsExpr.(*ast.Ident); ok {
|
|
345
|
+
if ident.Name == "_" {
|
|
346
|
+
// Empty slot for blank identifier
|
|
347
|
+
} else if needsVarRefVars[i] {
|
|
348
|
+
c.tsw.WriteLiterally("_varref_tmp_")
|
|
349
|
+
c.tsw.WriteLiterally(ident.Name)
|
|
350
|
+
} else {
|
|
351
|
+
c.WriteIdent(ident, false)
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
c.WriteValueExpr(lhsExpr)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
c.tsw.WriteLiterally("] = ")
|
|
358
|
+
c.WriteValueExpr(callExpr)
|
|
359
|
+
c.tsw.WriteLine("")
|
|
360
|
+
|
|
361
|
+
// Now declare the VarRef-wrapped variables
|
|
362
|
+
for i, lhsExpr := range lhs {
|
|
363
|
+
if ident, ok := lhsExpr.(*ast.Ident); ok && ident.Name != "_" && needsVarRefVars[i] {
|
|
364
|
+
c.tsw.WriteLiterally("let ")
|
|
365
|
+
c.WriteIdent(ident, false)
|
|
366
|
+
c.tsw.WriteLiterally(" = $.varRef(_varref_tmp_")
|
|
367
|
+
c.tsw.WriteLiterally(ident.Name)
|
|
368
|
+
// Add non-null assertion to handle cases where the tuple type includes null
|
|
369
|
+
c.tsw.WriteLiterally("!)")
|
|
370
|
+
c.tsw.WriteLine("")
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return nil
|
|
374
|
+
}
|
|
375
|
+
|
|
308
376
|
c.tsw.WriteLiterally("let [")
|
|
309
377
|
|
|
310
378
|
for i, lhsExpr := range lhs {
|
|
@@ -536,3 +604,59 @@ func (c *GoToTSCompiler) writeMapLookupWithExists(lhs []ast.Expr, indexExpr *ast
|
|
|
536
604
|
|
|
537
605
|
return nil
|
|
538
606
|
}
|
|
607
|
+
|
|
608
|
+
// writeTypeShadowedAssignment handles the case where a variable name shadows a type name
|
|
609
|
+
// used in its initialization (e.g., field := field{...}).
|
|
610
|
+
// In TypeScript, `let field = new field({...})` fails because the variable shadows the class
|
|
611
|
+
// before initialization due to the Temporal Dead Zone (TDZ). The TDZ extends from the
|
|
612
|
+
// start of the block scope to the point of initialization, so even capturing the type
|
|
613
|
+
// reference before the `let` declaration doesn't work - they're in the same block.
|
|
614
|
+
//
|
|
615
|
+
// We solve this by renaming the variable to avoid the conflict entirely:
|
|
616
|
+
//
|
|
617
|
+
// let field_ = $.markAsStructValue(new field({...}));
|
|
618
|
+
//
|
|
619
|
+
// Then we need to track that all subsequent references to `field` should use `field_`.
|
|
620
|
+
// This is stored in the analysis NodeInfo.IdentifierMapping.
|
|
621
|
+
func (c *GoToTSCompiler) writeTypeShadowedAssignment(exp *ast.AssignStmt, origName, renamedVar string) error {
|
|
622
|
+
if len(exp.Lhs) != 1 || len(exp.Rhs) != 1 {
|
|
623
|
+
return fmt.Errorf("type shadowing assignment must have exactly 1 LHS and 1 RHS")
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
lhsIdent, ok := exp.Lhs[0].(*ast.Ident)
|
|
627
|
+
if !ok {
|
|
628
|
+
return fmt.Errorf("type shadowing assignment LHS must be an identifier")
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Check if this variable needs VarRef
|
|
632
|
+
obj := c.objectOfIdent(lhsIdent)
|
|
633
|
+
needsVarRef := obj != nil && c.analysis.NeedsVarRef(obj)
|
|
634
|
+
|
|
635
|
+
// Store the mapping so that subsequent references to this variable use the renamed version
|
|
636
|
+
if obj != nil {
|
|
637
|
+
c.renamedVars[obj] = renamedVar
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if needsVarRef {
|
|
641
|
+
// For VarRef'd variables:
|
|
642
|
+
// let field_ = $.varRef($.markAsStructValue(new field({...})))
|
|
643
|
+
c.tsw.WriteLiterally("let ")
|
|
644
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(renamedVar))
|
|
645
|
+
c.tsw.WriteLiterally(" = $.varRef(")
|
|
646
|
+
if err := c.WriteValueExpr(exp.Rhs[0]); err != nil {
|
|
647
|
+
return err
|
|
648
|
+
}
|
|
649
|
+
c.tsw.WriteLiterally(")")
|
|
650
|
+
} else {
|
|
651
|
+
// For non-VarRef variables:
|
|
652
|
+
// let field_ = $.markAsStructValue(new field({...}))
|
|
653
|
+
c.tsw.WriteLiterally("let ")
|
|
654
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(renamedVar))
|
|
655
|
+
c.tsw.WriteLiterally(" = ")
|
|
656
|
+
if err := c.WriteValueExpr(exp.Rhs[0]); err != nil {
|
|
657
|
+
return err
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return nil
|
|
662
|
+
}
|
package/compiler/stmt-range.go
CHANGED
|
@@ -171,7 +171,7 @@ func (c *GoToTSCompiler) writeStringRange(exp *ast.RangeStmt) error {
|
|
|
171
171
|
|
|
172
172
|
if exp.Value != nil {
|
|
173
173
|
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
174
|
-
c.tsw.WriteLiterally("
|
|
174
|
+
c.tsw.WriteLiterally("let ")
|
|
175
175
|
c.WriteIdent(ident, false)
|
|
176
176
|
c.tsw.WriteLiterally(" = _runes[")
|
|
177
177
|
c.tsw.WriteLiterally(indexVarName)
|
|
@@ -236,7 +236,7 @@ func (c *GoToTSCompiler) writeArraySliceWithKeyValue(exp *ast.RangeStmt, indexVa
|
|
|
236
236
|
c.tsw.WriteLine("")
|
|
237
237
|
|
|
238
238
|
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
239
|
-
c.tsw.WriteLiterally("
|
|
239
|
+
c.tsw.WriteLiterally("let ")
|
|
240
240
|
c.WriteIdent(ident, false)
|
|
241
241
|
c.tsw.WriteLiterally(" = ")
|
|
242
242
|
if err := c.writeArraySliceExpression(exp.X, isPointer); err != nil {
|
package/compiler/stmt.go
CHANGED
|
@@ -167,9 +167,17 @@ func (c *GoToTSCompiler) WriteStmtIncDec(stmt *ast.IncDecStmt) error {
|
|
|
167
167
|
func (c *GoToTSCompiler) WriteStmtBranch(stmt *ast.BranchStmt) error {
|
|
168
168
|
switch stmt.Tok {
|
|
169
169
|
case token.BREAK:
|
|
170
|
-
|
|
170
|
+
if stmt.Label != nil {
|
|
171
|
+
c.tsw.WriteLinef("break %s", stmt.Label.Name)
|
|
172
|
+
} else {
|
|
173
|
+
c.tsw.WriteLine("break")
|
|
174
|
+
}
|
|
171
175
|
case token.CONTINUE:
|
|
172
|
-
|
|
176
|
+
if stmt.Label != nil {
|
|
177
|
+
c.tsw.WriteLinef("continue %s", stmt.Label.Name)
|
|
178
|
+
} else {
|
|
179
|
+
c.tsw.WriteLine("continue")
|
|
180
|
+
}
|
|
173
181
|
case token.GOTO:
|
|
174
182
|
// TypeScript doesn't support goto, but we can handle it by skipping it
|
|
175
183
|
// since the labeled statement restructuring should handle the control flow
|
|
@@ -368,6 +376,15 @@ func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
|
|
|
368
376
|
return nil
|
|
369
377
|
}
|
|
370
378
|
|
|
379
|
+
// Defensive semicolon: if the expression will start with '(' in TypeScript,
|
|
380
|
+
// prepend a semicolon to prevent JavaScript from treating the previous line
|
|
381
|
+
// as a function call. This happens when:
|
|
382
|
+
// 1. CallExpr where Fun itself will be parenthesized (e.g., (await fn())())
|
|
383
|
+
// 2. Array/slice literals starting with '['
|
|
384
|
+
if c.needsDefensiveSemicolon(exp.X) {
|
|
385
|
+
c.tsw.WriteLiterally(";")
|
|
386
|
+
}
|
|
387
|
+
|
|
371
388
|
// Handle other expression statements
|
|
372
389
|
if err := c.WriteValueExpr(exp.X); err != nil { // Expression statement evaluates a value
|
|
373
390
|
return err
|
|
@@ -475,17 +492,10 @@ func (c *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
|
|
|
475
492
|
}
|
|
476
493
|
c.tsw.WriteLiterally(") ")
|
|
477
494
|
|
|
478
|
-
if err := c.
|
|
495
|
+
if err := c.writeIfBody(exp); err != nil {
|
|
479
496
|
return err
|
|
480
497
|
}
|
|
481
498
|
|
|
482
|
-
if exp.Else != nil {
|
|
483
|
-
c.tsw.WriteLiterally(" else ")
|
|
484
|
-
if err := c.WriteStmt(exp.Else); err != nil {
|
|
485
|
-
return err
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
499
|
c.tsw.Indent(-1)
|
|
490
500
|
c.tsw.WriteLine("}")
|
|
491
501
|
return nil
|
|
@@ -498,14 +508,31 @@ func (c *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
|
|
|
498
508
|
}
|
|
499
509
|
c.tsw.WriteLiterally(") ")
|
|
500
510
|
|
|
501
|
-
|
|
511
|
+
return c.writeIfBody(exp)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// writeIfBody writes the if body and optional else clause, handling newline suppression.
|
|
515
|
+
func (c *GoToTSCompiler) writeIfBody(exp *ast.IfStmt) error {
|
|
516
|
+
hasElse := exp.Else != nil
|
|
517
|
+
if err := c.WriteStmtBlock(exp.Body, hasElse); err != nil {
|
|
502
518
|
return err
|
|
503
519
|
}
|
|
504
520
|
|
|
505
|
-
if
|
|
521
|
+
if hasElse {
|
|
506
522
|
c.tsw.WriteLiterally(" else ")
|
|
507
|
-
|
|
508
|
-
|
|
523
|
+
switch elseStmt := exp.Else.(type) {
|
|
524
|
+
case *ast.BlockStmt:
|
|
525
|
+
if err := c.WriteStmtBlock(elseStmt, false); err != nil {
|
|
526
|
+
return err
|
|
527
|
+
}
|
|
528
|
+
case *ast.IfStmt:
|
|
529
|
+
if err := c.WriteStmtIf(elseStmt); err != nil {
|
|
530
|
+
return err
|
|
531
|
+
}
|
|
532
|
+
default:
|
|
533
|
+
if err := c.WriteStmt(exp.Else); err != nil {
|
|
534
|
+
return err
|
|
535
|
+
}
|
|
509
536
|
}
|
|
510
537
|
}
|
|
511
538
|
|
|
@@ -574,6 +601,12 @@ func (c *GoToTSCompiler) WriteStmtReturn(exp *ast.ReturnStmt) error {
|
|
|
574
601
|
}
|
|
575
602
|
}
|
|
576
603
|
}
|
|
604
|
+
|
|
605
|
+
// Special handling for primitive types that implement error interface
|
|
606
|
+
if c.writePrimitiveErrorWrapperIfNeeded(exp, res, i) {
|
|
607
|
+
continue
|
|
608
|
+
}
|
|
609
|
+
|
|
577
610
|
if err := c.WriteValueExpr(res); err != nil { // Return results are values
|
|
578
611
|
return err
|
|
579
612
|
}
|
|
@@ -586,6 +619,84 @@ func (c *GoToTSCompiler) WriteStmtReturn(exp *ast.ReturnStmt) error {
|
|
|
586
619
|
return nil
|
|
587
620
|
}
|
|
588
621
|
|
|
622
|
+
// writePrimitiveErrorWrapperIfNeeded checks if a return value is a primitive type
|
|
623
|
+
// that implements the error interface, and if so, wraps it with $.wrapPrimitiveError.
|
|
624
|
+
// Returns true if the wrapper was written, false otherwise.
|
|
625
|
+
func (c *GoToTSCompiler) writePrimitiveErrorWrapperIfNeeded(retStmt *ast.ReturnStmt, res ast.Expr, resultIndex int) bool {
|
|
626
|
+
// Get the expected return type for this position
|
|
627
|
+
nodeInfo := c.analysis.NodeData[retStmt]
|
|
628
|
+
if nodeInfo == nil || nodeInfo.EnclosingFuncDecl == nil {
|
|
629
|
+
return false
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
funcDecl := nodeInfo.EnclosingFuncDecl
|
|
633
|
+
if funcDecl.Type.Results == nil {
|
|
634
|
+
return false
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Find the expected return type for this result index
|
|
638
|
+
var expectedType types.Type
|
|
639
|
+
resultIdx := 0
|
|
640
|
+
for _, field := range funcDecl.Type.Results.List {
|
|
641
|
+
count := len(field.Names)
|
|
642
|
+
if count == 0 {
|
|
643
|
+
count = 1
|
|
644
|
+
}
|
|
645
|
+
for j := 0; j < count; j++ {
|
|
646
|
+
if resultIdx == resultIndex {
|
|
647
|
+
expectedType = c.pkg.TypesInfo.TypeOf(field.Type)
|
|
648
|
+
break
|
|
649
|
+
}
|
|
650
|
+
resultIdx++
|
|
651
|
+
}
|
|
652
|
+
if expectedType != nil {
|
|
653
|
+
break
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if expectedType == nil {
|
|
658
|
+
return false
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Check if the expected type is the error interface
|
|
662
|
+
if iface, ok := expectedType.Underlying().(*types.Interface); !ok || iface.String() != "interface{Error() string}" {
|
|
663
|
+
return false
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Get the actual type of the return expression
|
|
667
|
+
actualType := c.pkg.TypesInfo.TypeOf(res)
|
|
668
|
+
if actualType == nil {
|
|
669
|
+
return false
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Check if the actual type is a wrapper type (named type with basic underlying type)
|
|
673
|
+
if !c.isWrapperType(actualType) {
|
|
674
|
+
return false
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Check if the actual type has an Error() method
|
|
678
|
+
if !c.typeHasMethods(actualType, "Error") {
|
|
679
|
+
return false
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Get the qualified type name for the Error function
|
|
683
|
+
typeName := c.getQualifiedTypeName(actualType)
|
|
684
|
+
if typeName == "" {
|
|
685
|
+
return false
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Write: $.wrapPrimitiveError(value, TypeName_Error)
|
|
689
|
+
c.tsw.WriteLiterally("$.wrapPrimitiveError(")
|
|
690
|
+
if err := c.WriteValueExpr(res); err != nil {
|
|
691
|
+
return false
|
|
692
|
+
}
|
|
693
|
+
c.tsw.WriteLiterally(", ")
|
|
694
|
+
c.tsw.WriteLiterally(typeName)
|
|
695
|
+
c.tsw.WriteLiterally("_Error)")
|
|
696
|
+
|
|
697
|
+
return true
|
|
698
|
+
}
|
|
699
|
+
|
|
589
700
|
// WriteStmtBlock translates a Go block statement (`ast.BlockStmt`), typically
|
|
590
701
|
// `{ ...stmts... }`, into its TypeScript equivalent, carefully preserving
|
|
591
702
|
// comments and blank lines to maintain code readability and structure.
|
|
@@ -1129,3 +1240,38 @@ func (c *GoToTSCompiler) substituteExprForShadowing(expr ast.Expr, shadowingInfo
|
|
|
1129
1240
|
func (c *GoToTSCompiler) isBuiltinFunction(name string) bool {
|
|
1130
1241
|
return builtinFunctions[name]
|
|
1131
1242
|
}
|
|
1243
|
+
|
|
1244
|
+
// needsDefensiveSemicolon determines if an expression will generate TypeScript
|
|
1245
|
+
// code starting with '(' or '[', which would require a defensive semicolon to
|
|
1246
|
+
// prevent JavaScript from treating the previous line as a function call.
|
|
1247
|
+
func (c *GoToTSCompiler) needsDefensiveSemicolon(expr ast.Expr) bool {
|
|
1248
|
+
switch e := expr.(type) {
|
|
1249
|
+
case *ast.CallExpr:
|
|
1250
|
+
// Check if the function being called will be parenthesized
|
|
1251
|
+
// This happens when Fun is itself a CallExpr, TypeAssertExpr, or other complex expression
|
|
1252
|
+
switch e.Fun.(type) {
|
|
1253
|
+
case *ast.CallExpr:
|
|
1254
|
+
// (fn())() - needs defensive semicolon
|
|
1255
|
+
return true
|
|
1256
|
+
case *ast.TypeAssertExpr:
|
|
1257
|
+
// (x.(T))() - needs defensive semicolon
|
|
1258
|
+
return true
|
|
1259
|
+
case *ast.IndexExpr:
|
|
1260
|
+
// Could generate (arr[i])() if indexed result is called
|
|
1261
|
+
// But typically doesn't need defensive semicolon as arr[i]() is fine
|
|
1262
|
+
return false
|
|
1263
|
+
case *ast.ParenExpr:
|
|
1264
|
+
// Already parenthesized - needs defensive semicolon
|
|
1265
|
+
return true
|
|
1266
|
+
}
|
|
1267
|
+
case *ast.CompositeLit:
|
|
1268
|
+
// Array/slice literals start with '['
|
|
1269
|
+
if _, isArray := e.Type.(*ast.ArrayType); isArray {
|
|
1270
|
+
return true
|
|
1271
|
+
}
|
|
1272
|
+
case *ast.ParenExpr:
|
|
1273
|
+
// Parenthesized expressions start with '('
|
|
1274
|
+
return true
|
|
1275
|
+
}
|
|
1276
|
+
return false
|
|
1277
|
+
}
|
package/compiler/type-info.go
CHANGED
|
@@ -26,11 +26,9 @@ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
|
|
|
26
26
|
underlying := typ.Underlying()
|
|
27
27
|
switch t := underlying.(type) {
|
|
28
28
|
case *types.Basic:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", tsTypeName)
|
|
29
|
+
// Use Go type name (e.g., "int") not TypeScript type name (e.g., "number")
|
|
30
|
+
// The reflect system needs Go type names to correctly determine Kind()
|
|
31
|
+
c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", t.Name())
|
|
34
32
|
// Note: The original 'case *types.Named:' here for 'underlying' is intentionally omitted.
|
|
35
33
|
// If typ.Underlying() is *types.Named (e.g. type T1 MyInt; type T2 T1;),
|
|
36
34
|
// then writeTypeInfoObject(typ.Underlying()) would be called in some contexts,
|
package/compiler/type-utils.go
CHANGED
|
@@ -25,11 +25,50 @@ func (c *GoToTSCompiler) isRuneSliceType(t types.Type) bool {
|
|
|
25
25
|
return false
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
// isStringType checks if a type is string
|
|
28
|
+
// isStringType checks if a type is string or could be string (e.g., type parameter)
|
|
29
29
|
func (c *GoToTSCompiler) isStringType(t types.Type) bool {
|
|
30
30
|
if basic, isBasic := t.Underlying().(*types.Basic); isBasic {
|
|
31
31
|
return basic.Kind() == types.String || basic.Kind() == types.UntypedString
|
|
32
32
|
}
|
|
33
|
+
// Handle type parameters (e.g., Bytes ~[]byte | ~string)
|
|
34
|
+
if typeParam, isTypeParam := t.(*types.TypeParam); isTypeParam {
|
|
35
|
+
constraint := typeParam.Constraint()
|
|
36
|
+
if constraintIface, ok := constraint.(*types.Interface); ok {
|
|
37
|
+
return c.constraintIncludesString(constraintIface)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// constraintIncludesString checks if a type constraint includes string
|
|
44
|
+
func (c *GoToTSCompiler) constraintIncludesString(constraint *types.Interface) bool {
|
|
45
|
+
// Check if the constraint has type terms that include string
|
|
46
|
+
if constraint.IsMethodSet() {
|
|
47
|
+
return false // Pure method interface, no type terms
|
|
48
|
+
}
|
|
49
|
+
// For union constraints like []byte | string, check each term
|
|
50
|
+
for i := 0; i < constraint.NumEmbeddeds(); i++ {
|
|
51
|
+
embedded := constraint.EmbeddedType(i)
|
|
52
|
+
// Check if embedded is a union
|
|
53
|
+
if union, isUnion := embedded.(*types.Union); isUnion {
|
|
54
|
+
for j := 0; j < union.Len(); j++ {
|
|
55
|
+
term := union.Term(j)
|
|
56
|
+
termType := term.Type()
|
|
57
|
+
// Check if term is string or ~string
|
|
58
|
+
if basic, isBasic := termType.Underlying().(*types.Basic); isBasic {
|
|
59
|
+
if basic.Kind() == types.String {
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Check direct embedded type
|
|
66
|
+
if basic, isBasic := embedded.Underlying().(*types.Basic); isBasic {
|
|
67
|
+
if basic.Kind() == types.String {
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
33
72
|
return false
|
|
34
73
|
}
|
|
35
74
|
|
package/compiler/type.go
CHANGED
|
@@ -21,6 +21,9 @@ const (
|
|
|
21
21
|
// GoTypeContextVariadicParam is used when translating types for variadic parameter elements.
|
|
22
22
|
// This affects how interface{} types are handled (no null prefix).
|
|
23
23
|
GoTypeContextVariadicParam
|
|
24
|
+
// GoTypeContextGenericTypeArg is used when translating type arguments for generic types.
|
|
25
|
+
// This affects how function types are handled (no | null suffix for func() types).
|
|
26
|
+
GoTypeContextGenericTypeArg
|
|
24
27
|
)
|
|
25
28
|
|
|
26
29
|
// WriteGoType is the main dispatcher for translating Go types to their TypeScript
|
|
@@ -68,7 +71,7 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type, context GoTypeContext) {
|
|
|
68
71
|
c.WriteInterfaceType(t, nil) // No ast.InterfaceType available here
|
|
69
72
|
}
|
|
70
73
|
case *types.Signature:
|
|
71
|
-
c.WriteSignatureType(t)
|
|
74
|
+
c.WriteSignatureType(t, context)
|
|
72
75
|
case *types.Struct:
|
|
73
76
|
c.WriteStructType(t)
|
|
74
77
|
case *types.Alias:
|
|
@@ -249,7 +252,8 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
|
|
|
249
252
|
typePkg := t.Obj().Pkg()
|
|
250
253
|
if typePkg != nil && typePkg != c.pkg.Types {
|
|
251
254
|
// This type is from an imported package, find the import alias
|
|
252
|
-
|
|
255
|
+
alias, found := c.resolveImportAlias(typePkg)
|
|
256
|
+
if found && alias != "" {
|
|
253
257
|
// Write the qualified name: importAlias.TypeName
|
|
254
258
|
c.tsw.WriteLiterally(alias)
|
|
255
259
|
c.tsw.WriteLiterally(".")
|
|
@@ -262,7 +266,8 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
|
|
|
262
266
|
if i > 0 {
|
|
263
267
|
c.tsw.WriteLiterally(", ")
|
|
264
268
|
}
|
|
265
|
-
|
|
269
|
+
// Use GenericTypeArg context to avoid adding | null to function types
|
|
270
|
+
c.WriteGoType(t.TypeArgs().At(i), GoTypeContextGenericTypeArg)
|
|
266
271
|
}
|
|
267
272
|
c.tsw.WriteLiterally(">")
|
|
268
273
|
}
|
|
@@ -285,7 +290,8 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
|
|
|
285
290
|
if i > 0 {
|
|
286
291
|
c.tsw.WriteLiterally(", ")
|
|
287
292
|
}
|
|
288
|
-
|
|
293
|
+
// Use GenericTypeArg context to avoid adding | null to function types
|
|
294
|
+
c.WriteGoType(t.TypeArgs().At(i), GoTypeContextGenericTypeArg)
|
|
289
295
|
}
|
|
290
296
|
c.tsw.WriteLiterally(">")
|
|
291
297
|
}
|
|
@@ -392,19 +398,39 @@ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
|
|
|
392
398
|
if isAsync {
|
|
393
399
|
c.tsw.WriteLiterally("Promise<")
|
|
394
400
|
}
|
|
395
|
-
|
|
401
|
+
|
|
402
|
+
// Count total number of return values (each field may have multiple names like "a, b int")
|
|
403
|
+
totalResults := 0
|
|
404
|
+
for _, field := range exp.Results.List {
|
|
405
|
+
count := len(field.Names)
|
|
406
|
+
if count == 0 {
|
|
407
|
+
count = 1 // Unnamed return value
|
|
408
|
+
}
|
|
409
|
+
totalResults += count
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if totalResults == 1 {
|
|
396
413
|
// Single return type (named or unnamed)
|
|
397
414
|
// Use WriteTypeExpr to preserve qualified names like os.FileInfo
|
|
398
415
|
c.WriteTypeExpr(exp.Results.List[0].Type)
|
|
399
416
|
} else {
|
|
400
417
|
// Multiple return types -> tuple
|
|
401
418
|
c.tsw.WriteLiterally("[")
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
419
|
+
first := true
|
|
420
|
+
for _, field := range exp.Results.List {
|
|
421
|
+
// Each field may represent multiple return values (e.g., "a, b int")
|
|
422
|
+
count := len(field.Names)
|
|
423
|
+
if count == 0 {
|
|
424
|
+
count = 1 // Unnamed return value
|
|
425
|
+
}
|
|
426
|
+
for j := 0; j < count; j++ {
|
|
427
|
+
if !first {
|
|
428
|
+
c.tsw.WriteLiterally(", ")
|
|
429
|
+
}
|
|
430
|
+
first = false
|
|
431
|
+
// Use WriteTypeExpr to preserve qualified names like os.FileInfo
|
|
432
|
+
c.WriteTypeExpr(field.Type)
|
|
405
433
|
}
|
|
406
|
-
// Use WriteTypeExpr to preserve qualified names like os.FileInfo
|
|
407
|
-
c.WriteTypeExpr(field.Type)
|
|
408
434
|
}
|
|
409
435
|
c.tsw.WriteLiterally("]")
|
|
410
436
|
}
|
|
@@ -440,7 +466,11 @@ func (c *GoToTSCompiler) WriteInterfaceType(t *types.Interface, astNode *ast.Int
|
|
|
440
466
|
|
|
441
467
|
// WriteSignatureType translates a Go function signature to its TypeScript equivalent.
|
|
442
468
|
// It generates (param1: type1, param2: type2, ...): returnType for function types.
|
|
443
|
-
|
|
469
|
+
// The context parameter determines whether to add | null suffix:
|
|
470
|
+
// - GoTypeContextGeneral: Add | null (function values can be nil in Go)
|
|
471
|
+
// - GoTypeContextFunctionReturn: Add | null (function return values can be nil)
|
|
472
|
+
// - Other contexts (like type arguments): Don't add | null
|
|
473
|
+
func (c *GoToTSCompiler) WriteSignatureType(t *types.Signature, context GoTypeContext) {
|
|
444
474
|
c.tsw.WriteLiterally("(")
|
|
445
475
|
c.tsw.WriteLiterally("(")
|
|
446
476
|
params := t.Params()
|
|
@@ -492,7 +522,15 @@ func (c *GoToTSCompiler) WriteSignatureType(t *types.Signature) {
|
|
|
492
522
|
}
|
|
493
523
|
c.tsw.WriteLiterally("]")
|
|
494
524
|
}
|
|
495
|
-
c.tsw.WriteLiterally(")
|
|
525
|
+
c.tsw.WriteLiterally(")")
|
|
526
|
+
|
|
527
|
+
// In Go, function values (not pointers to functions) can be nil.
|
|
528
|
+
// Add | null for general contexts and function return contexts.
|
|
529
|
+
// Don't add | null for other contexts like type arguments to avoid
|
|
530
|
+
// issues with generic types like atomic.Pointer[func()].
|
|
531
|
+
if context == GoTypeContextGeneral || context == GoTypeContextFunctionReturn {
|
|
532
|
+
c.tsw.WriteLiterally(" | null")
|
|
533
|
+
}
|
|
496
534
|
}
|
|
497
535
|
|
|
498
536
|
// writeInterfaceStructure translates a Go `types.Interface` into its TypeScript structural representation.
|
|
@@ -680,7 +718,7 @@ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode
|
|
|
680
718
|
func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
|
|
681
719
|
var typeStr strings.Builder
|
|
682
720
|
writer := NewTSCodeWriter(&typeStr)
|
|
683
|
-
tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
|
|
721
|
+
tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis, c.currentFilePath)
|
|
684
722
|
tempCompiler.WriteGoType(goType, GoTypeContextGeneral)
|
|
685
723
|
return typeStr.String()
|
|
686
724
|
}
|
|
@@ -692,7 +730,7 @@ func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
|
|
|
692
730
|
func (c *GoToTSCompiler) getASTTypeString(astType ast.Expr, goType types.Type) string {
|
|
693
731
|
var typeStr strings.Builder
|
|
694
732
|
writer := NewTSCodeWriter(&typeStr)
|
|
695
|
-
tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
|
|
733
|
+
tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis, c.currentFilePath)
|
|
696
734
|
|
|
697
735
|
if astType != nil {
|
|
698
736
|
// Use AST-based type writing to preserve qualified names
|
|
@@ -8,7 +8,14 @@ export declare function println(...args: any[]): void;
|
|
|
8
8
|
* Implementation of Go's built-in panic function
|
|
9
9
|
* @param args Arguments passed to panic
|
|
10
10
|
*/
|
|
11
|
-
export declare function panic(...args: any[]):
|
|
11
|
+
export declare function panic(...args: any[]): never;
|
|
12
|
+
/**
|
|
13
|
+
* Implementation of Go's built-in clear function.
|
|
14
|
+
* For slices, it sets all elements to their zero value.
|
|
15
|
+
* For maps, it deletes all entries.
|
|
16
|
+
* @param v The slice or map to clear
|
|
17
|
+
*/
|
|
18
|
+
export declare function clear<T>(v: T[] | Map<unknown, unknown> | null): void;
|
|
12
19
|
export type Bytes = Uint8Array | Slice<number>;
|
|
13
20
|
export declare function int(value: number): number;
|
|
14
21
|
/**
|
|
@@ -4,7 +4,13 @@ import { isSliceProxy } from './slice.js';
|
|
|
4
4
|
* @param args Arguments to print
|
|
5
5
|
*/
|
|
6
6
|
export function println(...args) {
|
|
7
|
-
|
|
7
|
+
if (args.length === 0) {
|
|
8
|
+
// Bun's console.log() with no args doesn't print a newline, so we explicitly print an empty string
|
|
9
|
+
console.log('');
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
console.log(...args);
|
|
13
|
+
}
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
16
|
* Implementation of Go's built-in panic function
|
|
@@ -13,6 +19,25 @@ export function println(...args) {
|
|
|
13
19
|
export function panic(...args) {
|
|
14
20
|
throw new Error(`panic: ${args.map((arg) => String(arg)).join(' ')}`);
|
|
15
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Implementation of Go's built-in clear function.
|
|
24
|
+
* For slices, it sets all elements to their zero value.
|
|
25
|
+
* For maps, it deletes all entries.
|
|
26
|
+
* @param v The slice or map to clear
|
|
27
|
+
*/
|
|
28
|
+
export function clear(v) {
|
|
29
|
+
if (v === null || v === undefined) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (v instanceof Map) {
|
|
33
|
+
v.clear();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(v)) {
|
|
37
|
+
v.fill(null);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
16
41
|
// int converts a value to a Go int type, handling proper signed integer conversion
|
|
17
42
|
// This ensures that values like 2147483648 (2^31) are properly handled according to Go semantics
|
|
18
43
|
export function int(value) {
|