goscript 0.0.77 → 0.0.79

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.
@@ -2756,23 +2756,25 @@ func (v *analysisVisitor) analyzeAllMethodsAsync() {
2756
2756
  // Topologically sort methods by their dependencies
2757
2757
  sorted, cycles := v.topologicalSortMethods(methodCalls)
2758
2758
 
2759
- // Mark methods in cycles - check if they contain async operations
2760
- // We can't rely on the call graph for methods in cycles (circular dependency),
2761
- // but we can still detect if they call async external methods
2762
- //
2763
- // We need to iterate multiple times because methods in cycles can call each other,
2764
- // and we need to propagate async status until no changes occur
2759
+ // Analyze methods in dependency order (dependencies analyzed before dependents)
2760
+ for _, methodKey := range sorted {
2761
+ v.analyzeMethodAsyncTopological(methodKey, methodCalls[methodKey])
2762
+ }
2763
+
2764
+ // Analyze methods in cycles after the acyclic portion of the graph is known.
2765
+ // This lets recursive methods observe async callees outside the cycle while
2766
+ // still converging over recursive edges inside the cycle.
2765
2767
  maxIterations := 10
2766
2768
  for range maxIterations {
2767
2769
  changed := false
2768
2770
 
2769
2771
  for _, methodKey := range cycles {
2770
- // For methods in cycles, we need to check their body directly for async operations
2771
2772
  pkg := v.analysis.AllPackages[methodKey.PackagePath]
2772
2773
  if pkg == nil && methodKey.PackagePath == v.pkg.Types.Path() {
2773
2774
  pkg = v.pkg
2774
2775
  }
2775
2776
 
2777
+ isAsync := false
2776
2778
  if pkg != nil {
2777
2779
  var funcDecl *ast.FuncDecl
2778
2780
  if methodKey.ReceiverType == "" {
@@ -2782,37 +2784,29 @@ func (v *analysisVisitor) analyzeAllMethodsAsync() {
2782
2784
  }
2783
2785
 
2784
2786
  if funcDecl != nil && funcDecl.Body != nil {
2785
- // Check if the method contains async operations (including calls to async external methods)
2786
- isAsync := v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
2787
-
2788
- // Check if status changed
2789
- if oldStatus, exists := v.analysis.MethodAsyncStatus[methodKey]; !exists || oldStatus != isAsync {
2790
- changed = true
2787
+ isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
2788
+ if !isAsync {
2789
+ for _, callee := range methodCalls[methodKey] {
2790
+ if calleeAsync, exists := v.analysis.MethodAsyncStatus[callee]; exists && calleeAsync {
2791
+ isAsync = true
2792
+ break
2793
+ }
2794
+ }
2791
2795
  }
2792
-
2793
- v.analysis.MethodAsyncStatus[methodKey] = isAsync
2794
- continue
2795
2796
  }
2796
2797
  }
2797
2798
 
2798
- // Fallback: mark as sync if we can't analyze the body
2799
- if _, exists := v.analysis.MethodAsyncStatus[methodKey]; !exists {
2800
- v.analysis.MethodAsyncStatus[methodKey] = false
2799
+ if oldStatus, exists := v.analysis.MethodAsyncStatus[methodKey]; !exists || oldStatus != isAsync {
2801
2800
  changed = true
2802
2801
  }
2802
+ v.analysis.MethodAsyncStatus[methodKey] = isAsync
2803
2803
  }
2804
2804
 
2805
- // If no changes in this iteration, we're done
2806
2805
  if !changed {
2807
2806
  break
2808
2807
  }
2809
2808
  }
2810
2809
 
2811
- // Analyze methods in dependency order (dependencies analyzed before dependents)
2812
- for _, methodKey := range sorted {
2813
- v.analyzeMethodAsyncTopological(methodKey, methodCalls[methodKey])
2814
- }
2815
-
2816
2810
  // Track async-returning variables BEFORE analyzing function literals
2817
2811
  // This detects variables assigned from higher-order functions with async function literal args
2818
2812
  // e.g., indirect := sync.OnceValue(asyncFunc)
@@ -3259,6 +3253,11 @@ func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *pa
3259
3253
  if n == nil {
3260
3254
  return false
3261
3255
  }
3256
+ if n != node {
3257
+ if _, ok := n.(*ast.FuncLit); ok {
3258
+ return false
3259
+ }
3260
+ }
3262
3261
 
3263
3262
  switch s := n.(type) {
3264
3263
  case *ast.SendStmt:
@@ -512,6 +512,403 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
512
512
  return nil
513
513
  }
514
514
 
515
+ // WriteNamedStructTypeSpec generates a TypeScript class for a named type whose
516
+ // underlying type is another named struct. The emitted class copies the field
517
+ // layout without inheriting the underlying named type's prototype methods.
518
+ func (c *GoToTSCompiler) WriteNamedStructTypeSpec(a *ast.TypeSpec, named *types.Named) error {
519
+ isInsideFunction := false
520
+ if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
521
+ isInsideFunction = nodeInfo.IsInsideFunction
522
+ }
523
+ if !isInsideFunction {
524
+ c.tsw.WriteLiterally("export ")
525
+ }
526
+ c.tsw.WriteLiterally("class ")
527
+ if err := c.WriteValueExpr(a.Name); err != nil {
528
+ return err
529
+ }
530
+
531
+ if a.TypeParams != nil {
532
+ c.WriteTypeParameters(a.TypeParams)
533
+ }
534
+
535
+ c.tsw.WriteLiterally(" ")
536
+ c.tsw.WriteLine("{")
537
+ c.tsw.Indent(1)
538
+
539
+ className := sanitizeIdentifier(a.Name.Name)
540
+ underlyingStruct, ok := named.Underlying().(*types.Struct)
541
+ if !ok {
542
+ return fmt.Errorf("underlying type of %s is not a struct", a.Name.Name)
543
+ }
544
+
545
+ underlyingType := c.pkg.TypesInfo.TypeOf(a.Type)
546
+ underlyingInitType := "{}"
547
+ if underlyingNamed, ok := underlyingType.(*types.Named); ok {
548
+ underlyingInitType = c.generateFlattenedInitTypeString(underlyingNamed, nil)
549
+ }
550
+ underlyingTypeName := c.getASTTypeString(a.Type, underlyingType)
551
+
552
+ for i := 0; i < underlyingStruct.NumFields(); i++ {
553
+ field := underlyingStruct.Field(i)
554
+ if field.Anonymous() {
555
+ continue
556
+ }
557
+ if !field.Exported() && field.Pkg() != c.pkg.Types {
558
+ continue
559
+ }
560
+ c.writeGetterSetter(field.Name(), field.Type(), nil, nil, nil)
561
+ }
562
+
563
+ for field := range underlyingStruct.Fields() {
564
+ if field.Anonymous() {
565
+ fieldKeyName := c.getEmbeddedFieldKeyName(field.Type())
566
+ c.writeGetterSetter(fieldKeyName, field.Type(), nil, nil, nil)
567
+ }
568
+ }
569
+
570
+ c.tsw.WriteLiterally("public _fields: {")
571
+ c.tsw.Indent(1)
572
+ c.tsw.WriteLine("")
573
+ for i := 0; i < underlyingStruct.NumFields(); i++ {
574
+ field := underlyingStruct.Field(i)
575
+ fieldKeyName := field.Name()
576
+ if field.Anonymous() {
577
+ fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
578
+ }
579
+ if fieldKeyName == "_" {
580
+ continue
581
+ }
582
+ c.tsw.WriteLinef("%s: $.VarRef<%s>;", fieldKeyName, c.getTypeString(field.Type()))
583
+ }
584
+ c.tsw.Indent(-1)
585
+ c.tsw.WriteLine("}")
586
+ c.tsw.WriteLine("")
587
+
588
+ c.tsw.WriteLinef("constructor(init?: Partial<%s>) {", underlyingInitType)
589
+ c.tsw.Indent(1)
590
+ c.tsw.WriteLinef("const base = new %s(init)", underlyingTypeName)
591
+ c.tsw.WriteLine("this._fields = base._fields")
592
+ c.tsw.Indent(-1)
593
+ c.tsw.WriteLine("}")
594
+ c.tsw.WriteLine("")
595
+
596
+ var cloneReturnType strings.Builder
597
+ cloneReturnType.WriteString(className)
598
+ if a.TypeParams != nil && len(a.TypeParams.List) > 0 {
599
+ cloneReturnType.WriteString("<")
600
+ first := true
601
+ for _, field := range a.TypeParams.List {
602
+ for _, name := range field.Names {
603
+ if !first {
604
+ cloneReturnType.WriteString(", ")
605
+ }
606
+ first = false
607
+ cloneReturnType.WriteString(name.Name)
608
+ }
609
+ }
610
+ cloneReturnType.WriteString(">")
611
+ }
612
+
613
+ c.tsw.WriteLinef("public clone(): %s {", cloneReturnType.String())
614
+ c.tsw.Indent(1)
615
+ c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType.String())
616
+ c.tsw.WriteLine("cloned._fields = {")
617
+ c.tsw.Indent(1)
618
+
619
+ firstFieldWritten := false
620
+ for i := 0; i < underlyingStruct.NumFields(); i++ {
621
+ field := underlyingStruct.Field(i)
622
+ fieldType := field.Type()
623
+ fieldKeyName := field.Name()
624
+ if field.Anonymous() {
625
+ fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
626
+ }
627
+ if fieldKeyName == "_" {
628
+ continue
629
+ }
630
+
631
+ if firstFieldWritten {
632
+ c.tsw.WriteLine(",")
633
+ }
634
+ c.writeClonedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
635
+ firstFieldWritten = true
636
+ }
637
+ if firstFieldWritten {
638
+ c.tsw.WriteLine("")
639
+ }
640
+
641
+ c.tsw.Indent(-1)
642
+ c.tsw.WriteLine("}")
643
+ c.tsw.WriteLine("return cloned")
644
+ c.tsw.Indent(-1)
645
+ c.tsw.WriteLine("}")
646
+
647
+ for _, fileSyntax := range c.pkg.Syntax {
648
+ for _, decl := range fileSyntax.Decls {
649
+ funcDecl, isFunc := decl.(*ast.FuncDecl)
650
+ if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
651
+ continue
652
+ }
653
+ recvType := funcDecl.Recv.List[0].Type
654
+ if starExpr, ok := recvType.(*ast.StarExpr); ok {
655
+ recvType = starExpr.X
656
+ }
657
+
658
+ var recvTypeName string
659
+ if ident, ok := recvType.(*ast.Ident); ok {
660
+ recvTypeName = sanitizeIdentifier(ident.Name)
661
+ } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
662
+ if ident, ok := indexExpr.X.(*ast.Ident); ok {
663
+ recvTypeName = sanitizeIdentifier(ident.Name)
664
+ }
665
+ }
666
+
667
+ if recvTypeName == className {
668
+ c.tsw.WriteLine("")
669
+ if err := c.WriteFuncDeclAsMethod(funcDecl); err != nil {
670
+ return err
671
+ }
672
+ }
673
+ }
674
+ }
675
+
676
+ seenPromotedFields := make(map[string]bool)
677
+ directMethods := make(map[string]bool)
678
+ for method := range named.Methods() {
679
+ sig := method.Type().(*types.Signature)
680
+ if sig.Recv() != nil {
681
+ recvType := sig.Recv().Type()
682
+ if namedRecv, ok := recvType.(*types.Named); ok && namedRecv.Obj() == named.Obj() {
683
+ directMethods[method.Name()] = true
684
+ } else if ptrRecv, ok := recvType.(*types.Pointer); ok {
685
+ if namedElem, ok := ptrRecv.Elem().(*types.Named); ok && namedElem.Obj() == named.Obj() {
686
+ directMethods[method.Name()] = true
687
+ }
688
+ }
689
+ }
690
+ }
691
+
692
+ for field := range underlyingStruct.Fields() {
693
+ if !field.Anonymous() {
694
+ continue
695
+ }
696
+
697
+ embeddedFieldType := field.Type()
698
+ embeddedFieldKeyName := c.getEmbeddedFieldKeyName(field.Type())
699
+
700
+ trueEmbeddedType := embeddedFieldType
701
+ if ptr, isPtr := trueEmbeddedType.(*types.Pointer); isPtr {
702
+ trueEmbeddedType = ptr.Elem()
703
+ }
704
+ if _, isNamed := trueEmbeddedType.(*types.Named); !isNamed {
705
+ continue
706
+ }
707
+
708
+ if namedEmbedded, ok := trueEmbeddedType.(*types.Named); ok {
709
+ if underlyingEmbeddedStruct, ok := namedEmbedded.Underlying().(*types.Struct); ok {
710
+ for promotedField := range underlyingEmbeddedStruct.Fields() {
711
+ if !promotedField.Exported() && promotedField.Pkg() != c.pkg.Types {
712
+ continue
713
+ }
714
+ promotedFieldName := promotedField.Name()
715
+ if seenPromotedFields[promotedFieldName] {
716
+ continue
717
+ }
718
+ conflict := false
719
+ for field := range underlyingStruct.Fields() {
720
+ if !field.Anonymous() && field.Name() == promotedFieldName {
721
+ conflict = true
722
+ break
723
+ }
724
+ }
725
+ if conflict {
726
+ continue
727
+ }
728
+
729
+ seenPromotedFields[promotedFieldName] = true
730
+ tsPromotedFieldType := c.getTypeString(promotedField.Type())
731
+ c.tsw.WriteLine("")
732
+ c.tsw.WriteLinef("public get %s(): %s {", promotedFieldName, tsPromotedFieldType)
733
+ c.tsw.Indent(1)
734
+ embeddedFieldTypeUnderlying := embeddedFieldType
735
+ if ptr, isPtr := embeddedFieldTypeUnderlying.(*types.Pointer); isPtr {
736
+ embeddedFieldTypeUnderlying = ptr.Elem()
737
+ }
738
+ if named, isNamed := embeddedFieldTypeUnderlying.(*types.Named); isNamed {
739
+ embeddedFieldTypeUnderlying = named.Underlying()
740
+ }
741
+ if _, isInterface := embeddedFieldTypeUnderlying.(*types.Interface); isInterface {
742
+ c.tsw.WriteLinef("return this.%s!.%s", embeddedFieldKeyName, promotedFieldName)
743
+ } else {
744
+ c.tsw.WriteLinef("return this.%s.%s", embeddedFieldKeyName, promotedFieldName)
745
+ }
746
+ c.tsw.Indent(-1)
747
+ c.tsw.WriteLine("}")
748
+ c.tsw.WriteLinef("public set %s(value: %s) {", promotedFieldName, tsPromotedFieldType)
749
+ c.tsw.Indent(1)
750
+ if _, isInterface := embeddedFieldTypeUnderlying.(*types.Interface); isInterface {
751
+ c.tsw.WriteLinef("this.%s!.%s = value", embeddedFieldKeyName, promotedFieldName)
752
+ } else {
753
+ c.tsw.WriteLinef("this.%s.%s = value", embeddedFieldKeyName, promotedFieldName)
754
+ }
755
+ c.tsw.Indent(-1)
756
+ c.tsw.WriteLine("}")
757
+ }
758
+ }
759
+ }
760
+
761
+ methodSetType := embeddedFieldType
762
+ if _, isPtr := embeddedFieldType.(*types.Pointer); !isPtr {
763
+ if _, isInterface := embeddedFieldType.Underlying().(*types.Interface); !isInterface {
764
+ methodSetType = types.NewPointer(embeddedFieldType)
765
+ }
766
+ }
767
+ embeddedMethodSet := types.NewMethodSet(methodSetType)
768
+ for methodSelection := range embeddedMethodSet.Methods() {
769
+ method := methodSelection.Obj().(*types.Func)
770
+ methodName := method.Name()
771
+
772
+ if len(methodSelection.Index()) == 1 && !directMethods[methodName] && !seenPromotedFields[methodName] {
773
+ conflictWithField := false
774
+ for field := range underlyingStruct.Fields() {
775
+ if !field.Anonymous() && field.Name() == methodName {
776
+ conflictWithField = true
777
+ break
778
+ }
779
+ }
780
+ if conflictWithField {
781
+ continue
782
+ }
783
+
784
+ seenPromotedFields[methodName] = true
785
+ sig := method.Type().(*types.Signature)
786
+ c.tsw.WriteLine("")
787
+ c.tsw.WriteLiterally("public ")
788
+ c.tsw.WriteLiterally(methodName)
789
+ c.tsw.WriteLiterally("(")
790
+ params := sig.Params()
791
+ paramNames := make([]string, params.Len())
792
+ for j := 0; j < params.Len(); j++ {
793
+ param := params.At(j)
794
+ paramName := param.Name()
795
+ if paramName == "" || paramName == "_" {
796
+ paramName = fmt.Sprintf("_p%d", j)
797
+ }
798
+ paramNames[j] = paramName
799
+ if j > 0 {
800
+ c.tsw.WriteLiterally(", ")
801
+ }
802
+ c.tsw.WriteLiterally(paramName)
803
+ c.tsw.WriteLiterally(": ")
804
+ c.WriteGoType(param.Type(), GoTypeContextGeneral)
805
+ }
806
+ c.tsw.WriteLiterally(")")
807
+ results := sig.Results()
808
+ if results.Len() > 0 {
809
+ c.tsw.WriteLiterally(": ")
810
+ if results.Len() == 1 {
811
+ c.WriteGoType(results.At(0).Type(), GoTypeContextFunctionReturn)
812
+ } else {
813
+ c.tsw.WriteLiterally("[")
814
+ for j := 0; j < results.Len(); j++ {
815
+ if j > 0 {
816
+ c.tsw.WriteLiterally(", ")
817
+ }
818
+ c.WriteGoType(results.At(j).Type(), GoTypeContextFunctionReturn)
819
+ }
820
+ c.tsw.WriteLiterally("]")
821
+ }
822
+ } else {
823
+ c.tsw.WriteLiterally(": void")
824
+ }
825
+ c.tsw.WriteLine(" {")
826
+ c.tsw.Indent(1)
827
+ if results.Len() > 0 {
828
+ c.tsw.WriteLiterally("return ")
829
+ }
830
+
831
+ assertionPrefix := "this.%s"
832
+ if _, isInterface := embeddedFieldType.Underlying().(*types.Interface); isInterface {
833
+ assertionPrefix = "this.%s!"
834
+ }
835
+ c.tsw.WriteLiterallyf(assertionPrefix+".%s(%s)", embeddedFieldKeyName, methodName, strings.Join(paramNames, ", "))
836
+
837
+ c.tsw.WriteLine("")
838
+ c.tsw.Indent(-1)
839
+ c.tsw.WriteLine("}")
840
+ }
841
+ }
842
+ }
843
+
844
+ c.tsw.WriteLine("")
845
+ c.tsw.WriteLine("// Register this type with the runtime type system")
846
+
847
+ structName := className
848
+ pkgPath := c.pkg.Types.Path()
849
+ pkgName := c.pkg.Types.Name()
850
+ if pkgPath != "" && pkgName != "main" {
851
+ structName = pkgPath + "." + className
852
+ } else if pkgName == "main" {
853
+ structName = "main." + className
854
+ }
855
+
856
+ c.tsw.WriteLine("static __typeInfo = $.registerStructType(")
857
+ c.tsw.WriteLinef(" %q,", structName)
858
+ c.tsw.WriteLinef(" new %s(),", className)
859
+ c.tsw.WriteLiterally(" [")
860
+
861
+ var structMethods []*types.Func
862
+ for method := range named.Methods() {
863
+ sig := method.Type().(*types.Signature)
864
+ recv := sig.Recv().Type()
865
+ if ptr, ok := recv.(*types.Pointer); ok {
866
+ recv = ptr.Elem()
867
+ }
868
+ if namedRecv, ok := recv.(*types.Named); ok && namedRecv.Obj() == named.Obj() {
869
+ structMethods = append(structMethods, method)
870
+ }
871
+ }
872
+ c.writeMethodSignatures(structMethods)
873
+ c.tsw.WriteLiterally("],")
874
+ c.tsw.WriteLine("")
875
+
876
+ c.tsw.WriteLinef(" %s,", className)
877
+ c.tsw.WriteLiterally(" {")
878
+ firstField := true
879
+ for i := 0; i < underlyingStruct.NumFields(); i++ {
880
+ field := underlyingStruct.Field(i)
881
+ fieldKeyName := field.Name()
882
+ if field.Anonymous() {
883
+ fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
884
+ }
885
+ if fieldKeyName == "_" {
886
+ continue
887
+ }
888
+ if !firstField {
889
+ c.tsw.WriteLiterally(", ")
890
+ }
891
+ firstField = false
892
+ c.tsw.WriteLiterallyf("%q: ", fieldKeyName)
893
+
894
+ tag := underlyingStruct.Tag(i)
895
+ if tag != "" {
896
+ c.tsw.WriteLiterally("{ type: ")
897
+ c.writeTypeInfoObject(field.Type())
898
+ c.tsw.WriteLiterallyf(", tag: %q }", tag)
899
+ } else {
900
+ c.writeTypeInfoObject(field.Type())
901
+ }
902
+ }
903
+ c.tsw.WriteLiterally("}")
904
+ c.tsw.WriteLine("")
905
+ c.tsw.WriteLine(");")
906
+
907
+ c.tsw.Indent(-1)
908
+ c.tsw.WriteLine("}")
909
+ return nil
910
+ }
911
+
515
912
  // generateFlattenedInitTypeString generates a TypeScript type string for the
516
913
  // initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
517
914
  // The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
package/compiler/spec.go CHANGED
@@ -366,6 +366,13 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
366
366
 
367
367
  if recvTypeName == className {
368
368
  c.tsw.WriteLiterally("export function ")
369
+ var isAsync bool
370
+ if obj := c.pkg.TypesInfo.Defs[funcDecl.Name]; obj != nil {
371
+ isAsync = c.analysis.IsAsyncFunc(obj)
372
+ }
373
+ if isAsync {
374
+ c.tsw.WriteLiterally("async ")
375
+ }
369
376
  c.tsw.WriteLiterally(className)
370
377
  c.tsw.WriteLiterally("_")
371
378
  c.tsw.WriteLiterally(funcDecl.Name.Name)
@@ -403,6 +410,9 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
403
410
  // Add return type
404
411
  if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
405
412
  c.tsw.WriteLiterally(": ")
413
+ if isAsync {
414
+ c.tsw.WriteLiterally("Promise<")
415
+ }
406
416
  if len(funcDecl.Type.Results.List) == 1 {
407
417
  c.WriteTypeExpr(funcDecl.Type.Results.List[0].Type)
408
418
  } else {
@@ -424,8 +434,15 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
424
434
  }
425
435
  c.tsw.WriteLiterally("]")
426
436
  }
437
+ if isAsync {
438
+ c.tsw.WriteLiterally(">")
439
+ }
427
440
  } else {
428
- c.tsw.WriteLiterally(": void")
441
+ if isAsync {
442
+ c.tsw.WriteLiterally(": Promise<void>")
443
+ } else {
444
+ c.tsw.WriteLiterally(": void")
445
+ }
429
446
  }
430
447
 
431
448
  c.tsw.WriteLine(" {")
@@ -476,6 +493,11 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
476
493
  default:
477
494
  // Check if this type has receiver methods
478
495
  if c.hasReceiverMethods(a.Name.Name) {
496
+ if named, ok := c.pkg.TypesInfo.Defs[a.Name].Type().(*types.Named); ok {
497
+ if _, isStruct := named.Underlying().(*types.Struct); isStruct {
498
+ return c.WriteNamedStructTypeSpec(a, named)
499
+ }
500
+ }
479
501
  return c.WriteNamedTypeWithMethods(a)
480
502
  }
481
503
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "goscript",
3
3
  "description": "Go to TypeScript transpiler",
4
- "version": "0.0.77",
4
+ "version": "0.0.79",
5
5
  "author": {
6
6
  "name": "Aperture Robotics LLC.",
7
7
  "email": "support@aperture.us",