goscript 0.0.7 → 0.0.10

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 (66) hide show
  1. package/README.md +13 -5
  2. package/builtin/builtin.ts +180 -0
  3. package/compiler/compile_expr.go +119 -25
  4. package/compiler/compile_spec.go +175 -2
  5. package/compiler/compile_stmt.go +99 -69
  6. package/compiler/writer.go +2 -2
  7. package/dist/builtin/builtin.d.ts +55 -0
  8. package/dist/builtin/builtin.js +126 -0
  9. package/dist/builtin/builtin.js.map +1 -1
  10. package/dist/compliance/tests/array_literal/array_literal.gs.js +1 -0
  11. package/dist/compliance/tests/array_literal/array_literal.gs.js.map +1 -1
  12. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +4 -0
  13. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +1 -1
  14. package/dist/compliance/tests/copy_independence/copy_independence.gs.js +3 -1
  15. package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +1 -1
  16. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.d.ts +1 -0
  17. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +37 -0
  18. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -0
  19. package/dist/compliance/tests/float64/float64.gs.js +1 -0
  20. package/dist/compliance/tests/float64/float64.gs.js.map +1 -1
  21. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.d.ts +1 -0
  22. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js +11 -0
  23. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js.map +1 -0
  24. package/dist/compliance/tests/func_literal/func_literal.gs.js.map +1 -1
  25. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +2 -0
  26. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +4 -0
  27. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +1 -1
  28. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.d.ts +1 -0
  29. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +34 -0
  30. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +1 -0
  31. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +8 -2
  32. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +1 -1
  33. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +6 -2
  34. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
  35. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +3 -1
  36. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +1 -1
  37. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +3 -0
  38. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +1 -1
  39. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +3 -0
  40. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +1 -1
  41. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +3 -0
  42. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +1 -1
  43. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +3 -0
  44. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +1 -1
  45. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +3 -0
  46. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +1 -1
  47. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +3 -0
  48. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +1 -1
  49. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +3 -1
  50. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +1 -1
  51. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +17 -16
  52. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +1 -1
  53. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +13 -13
  54. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +1 -1
  55. package/dist/compliance/tests/select_statement/select_statement.gs.js +119 -119
  56. package/dist/compliance/tests/select_statement/select_statement.gs.js.map +1 -1
  57. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +3 -1
  58. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +1 -1
  59. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js.map +1 -1
  60. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +3 -0
  61. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +1 -1
  62. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +3 -0
  63. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +1 -1
  64. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +3 -1
  65. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +1 -1
  66. package/package.json +2 -2
package/README.md CHANGED
@@ -463,11 +463,19 @@ Code is compiled with `GOARCH=js` and uses a 32-bit environment similar to wasm.
463
463
  All Go import paths are prefixed with `@go/` and can be imported in TypeScript:
464
464
 
465
465
  ```typescript
466
- import { MyFunction, MyStruct } from '@go/github.com/myorg/mypackage';
467
-
468
- MyFunction();
469
- let myThing = new MyStruct();
470
- myThing.DoSometing();
466
+ import { MyAsyncFunction, MyStruct } from '@go/github.com/myorg/mypackage';
467
+
468
+ // Example of importing and using a compiled Go async function
469
+ async function runGoCode() {
470
+ // Call an async function compiled from Go
471
+ const result = await MyAsyncFunction("input data");
472
+ console.log("Result from Go async function:", result);
473
+
474
+ // You can still use synchronous types and functions
475
+ let myThing = new MyStruct();
476
+ myThing.GetMyString();
477
+ runGoCode();
478
+ }
471
479
  ```
472
480
 
473
481
  ## License
@@ -46,6 +46,13 @@ export const cap = <T>(slice: Array<T> & { __capacity?: number }): number => {
46
46
  return slice.__capacity !== undefined ? slice.__capacity : slice.length
47
47
  }
48
48
 
49
+ /**
50
+ * Represents the Go error type (interface).
51
+ */
52
+ export type Error = {
53
+ Error(): string;
54
+ } | null;
55
+
49
56
  /**
50
57
  * Converts a string to an array of Unicode code points (runes).
51
58
  * @param str The input string.
@@ -111,6 +118,179 @@ export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
111
118
  return slice
112
119
  }
113
120
 
121
+ /**
122
+ * Represents the kinds of Go types that can be registered at runtime.
123
+ */
124
+ export enum TypeKind {
125
+ Struct = "struct",
126
+ Interface = "interface",
127
+ Basic = "basic",
128
+ Pointer = "pointer",
129
+ Slice = "slice",
130
+ Map = "map",
131
+ Channel = "channel",
132
+ Function = "function",
133
+ }
134
+
135
+ /**
136
+ * Represents type information for a Go type in the runtime.
137
+ */
138
+ export interface TypeInfo {
139
+ name: string;
140
+ kind: TypeKind;
141
+ zeroValue: any;
142
+ // For interfaces, the set of methods
143
+ methods?: Set<string>;
144
+ // For structs, the constructor
145
+ constructor?: new (...args: any[]) => any;
146
+ }
147
+
148
+ // Registry to store runtime type information
149
+ const typeRegistry = new Map<string, TypeInfo>();
150
+
151
+ /**
152
+ * Registers a type with the runtime type system.
153
+ *
154
+ * @param name The name of the type.
155
+ * @param kind The kind of the type.
156
+ * @param zeroValue The zero value for the type.
157
+ * @param methods Optional set of method names for interfaces.
158
+ * @param constructor Optional constructor for structs.
159
+ * @returns The type information object for chaining.
160
+ */
161
+ export const registerType = (
162
+ name: string,
163
+ kind: TypeKind,
164
+ zeroValue: any,
165
+ methods?: Set<string>,
166
+ constructor?: new (...args: any[]) => any
167
+ ): TypeInfo => {
168
+ const typeInfo: TypeInfo = {
169
+ name,
170
+ kind,
171
+ zeroValue,
172
+ methods,
173
+ constructor,
174
+ };
175
+ typeRegistry.set(name, typeInfo);
176
+ return typeInfo;
177
+ };
178
+
179
+ /**
180
+ * Represents the result of a type assertion.
181
+ */
182
+ export interface TypeAssertResult<T> {
183
+ value: T;
184
+ ok: boolean;
185
+ }
186
+
187
+ /**
188
+ * Performs a type assertion at runtime.
189
+ *
190
+ * @param value The value to assert.
191
+ * @param typeName The name of the target type.
192
+ * @returns An object with the asserted value and whether the assertion succeeded.
193
+ */
194
+ export function typeAssert<T>(value: any, typeName: string): TypeAssertResult<T> {
195
+ // Get the type information from the registry
196
+ const typeInfo = typeRegistry.get(typeName);
197
+ if (!typeInfo) {
198
+ console.warn(`Type information for '${typeName}' not found in registry.`);
199
+ return { value: null as unknown as T, ok: false };
200
+ }
201
+
202
+ // If value is null or undefined, assertion fails
203
+ if (value === null || value === undefined) {
204
+ return { value: typeInfo.zeroValue as T, ok: false };
205
+ }
206
+
207
+ // Check based on the kind of the target type
208
+ switch (typeInfo.kind) {
209
+ case TypeKind.Struct:
210
+ // For structs, use instanceof with the constructor
211
+ if (typeInfo.constructor && value instanceof typeInfo.constructor) {
212
+ return { value: value as T, ok: true };
213
+ }
214
+ break;
215
+
216
+ case TypeKind.Interface:
217
+ // For interfaces, check if the value has all the required methods
218
+ if (typeInfo.methods && typeof value === 'object') {
219
+ const allMethodsPresent = Array.from(typeInfo.methods).every(
220
+ (method) => typeof (value as any)[method] === 'function'
221
+ );
222
+ if (allMethodsPresent) {
223
+ return { value: value as T, ok: true };
224
+ }
225
+ }
226
+ break;
227
+
228
+ case TypeKind.Basic:
229
+ // For basic types, check if the value matches the expected JavaScript type
230
+ // This is a simple check for common basic types
231
+ const basicType = typeof value;
232
+ if (
233
+ basicType === 'string' ||
234
+ basicType === 'number' ||
235
+ basicType === 'boolean'
236
+ ) {
237
+ return { value: value as T, ok: true };
238
+ }
239
+ break;
240
+
241
+ case TypeKind.Pointer:
242
+ // For pointers, check if value is not null or undefined
243
+ // In Go, pointers can be nil which we represent as null/undefined in TS
244
+ if (value !== null && value !== undefined) {
245
+ return { value: value as T, ok: true };
246
+ }
247
+ break;
248
+
249
+ case TypeKind.Slice:
250
+ // For slices, check if the value is an array
251
+ if (Array.isArray(value)) {
252
+ return { value: value as T, ok: true };
253
+ }
254
+ break;
255
+
256
+ case TypeKind.Map:
257
+ // For maps, check if the value is a Map
258
+ if (value instanceof Map) {
259
+ return { value: value as T, ok: true };
260
+ }
261
+ break;
262
+
263
+ case TypeKind.Channel:
264
+ // For channels, check if the value has the required Channel interface methods
265
+ if (
266
+ typeof value === 'object' &&
267
+ value !== null &&
268
+ 'send' in value &&
269
+ 'receive' in value &&
270
+ 'close' in value &&
271
+ typeof value.send === 'function' &&
272
+ typeof value.receive === 'function' &&
273
+ typeof value.close === 'function'
274
+ ) {
275
+ return { value: value as T, ok: true };
276
+ }
277
+ break;
278
+
279
+ case TypeKind.Function:
280
+ // For functions, check if the value is a function
281
+ if (typeof value === 'function') {
282
+ return { value: value as T, ok: true };
283
+ }
284
+ break;
285
+
286
+ default:
287
+ console.warn(`Type assertion for kind '${typeInfo.kind}' not implemented.`);
288
+ }
289
+
290
+ // Assertion failed
291
+ return { value: typeInfo.zeroValue as T, ok: false };
292
+ }
293
+
114
294
  /**
115
295
  * Represents the result of a channel receive operation with 'ok' value
116
296
  */
@@ -7,6 +7,7 @@ import (
7
7
  "go/token"
8
8
  gtypes "go/types"
9
9
  "strconv"
10
+ "strings"
10
11
 
11
12
  gstypes "github.com/paralin/goscript/compiler/types"
12
13
  )
@@ -72,6 +73,9 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
72
73
  return c.WriteCompositeLitValue(exp)
73
74
  case *ast.KeyValueExpr:
74
75
  return c.WriteKeyValueExprValue(exp)
76
+ case *ast.TypeAssertExpr:
77
+ // Handle type assertion in an expression context
78
+ return c.WriteTypeAssertExpr(exp)
75
79
  case *ast.IndexExpr:
76
80
  // Handle map access: use Map.get() instead of brackets for reading values
77
81
  if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
@@ -121,11 +125,51 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
121
125
  }
122
126
  }
123
127
 
128
+ // WriteTypeAssertExpr writes a type assertion expression.
129
+ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
130
+ // Get the type name string for the asserted type
131
+ typeName := c.getTypeNameString(exp.Type)
132
+
133
+ // Generate a call to goscript.typeAssert
134
+ c.tsw.WriteLiterally("goscript.typeAssert<")
135
+ c.WriteTypeExpr(exp.Type) // Write the asserted type for the generic
136
+ c.tsw.WriteLiterally(">(")
137
+ if err := c.WriteValueExpr(exp.X); err != nil { // The interface expression
138
+ return fmt.Errorf("failed to write interface expression in type assertion expression: %w", err)
139
+ }
140
+ c.tsw.WriteLiterally(", ")
141
+ c.tsw.WriteLiterally(fmt.Sprintf("'%s'", typeName))
142
+ c.tsw.WriteLiterally(").value") // Access the value field directly in expression context
143
+ return nil
144
+ }
145
+
146
+ // getTypeNameString returns the string representation of a type name
147
+ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
148
+ switch t := typeExpr.(type) {
149
+ case *ast.Ident:
150
+ return t.Name
151
+ case *ast.SelectorExpr:
152
+ // For imported types like pkg.Type
153
+ if ident, ok := t.X.(*ast.Ident); ok {
154
+ return fmt.Sprintf("%s.%s", ident.Name, t.Sel.Name)
155
+ }
156
+ }
157
+ // Default case, use a placeholder for complex types
158
+ return "unknown"
159
+ }
160
+
124
161
  // --- Exported Node-Specific Writers ---
125
162
 
126
163
  // WriteIdentType writes an identifier used as a type.
127
164
  func (c *GoToTSCompiler) WriteIdentType(exp *ast.Ident) {
128
165
  name := exp.Name
166
+
167
+ // Special case for the built-in error interface
168
+ if name == "error" {
169
+ c.tsw.WriteLiterally("goscript.Error")
170
+ return
171
+ }
172
+
129
173
  if tsname, ok := gstypes.GoBuiltinToTypescript(name); ok {
130
174
  name = tsname
131
175
  } else {
@@ -150,9 +194,13 @@ func (c *GoToTSCompiler) WriteIdentType(exp *ast.Ident) {
150
194
  c.tsw.WriteLiterally(name)
151
195
  }
152
196
 
153
- // WriteIdentValue writes an identifier used as a value (variable, function name).
197
+ // WriteIdentValue writes an identifier used as a value (variable, function name, nil).
154
198
  func (c *GoToTSCompiler) WriteIdentValue(exp *ast.Ident) {
155
- c.tsw.WriteLiterally(exp.Name)
199
+ if exp.Name == "nil" {
200
+ c.tsw.WriteLiterally("null")
201
+ } else {
202
+ c.tsw.WriteLiterally(exp.Name)
203
+ }
156
204
  }
157
205
 
158
206
  // WriteSelectorExprType writes a selector expression used as a type (e.g., pkg.Type).
@@ -205,32 +253,47 @@ func (c *GoToTSCompiler) WriteStructType(exp *ast.StructType) {
205
253
 
206
254
  // WriteInterfaceType writes an interface type definition.
207
255
  func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
208
- if exp.Methods == nil || exp.Methods.NumFields() == 0 {
209
- c.tsw.WriteLiterally("{}")
210
- return
211
- }
212
- c.tsw.WriteLine("{")
213
- c.tsw.Indent(1)
214
- for _, method := range exp.Methods.List {
215
- if len(method.Names) > 0 {
216
- // Keep original Go casing for method names
217
- methodName := method.Names[0]
218
- c.WriteIdentValue(methodName)
219
-
220
- // Method signature is a FuncType
221
- if funcType, ok := method.Type.(*ast.FuncType); ok {
222
- c.WriteFuncType(funcType, false) // Interface method signatures are not async
256
+ var embeddedInterfaces []string
257
+ var methods []*ast.Field
258
+
259
+ if exp.Methods != nil {
260
+ for _, method := range exp.Methods.List {
261
+ if len(method.Names) > 0 {
262
+ // Named method
263
+ methods = append(methods, method)
223
264
  } else {
224
- // Should not happen for valid interfaces, but handle defensively
225
- c.tsw.WriteCommentInline("unexpected method type")
265
+ // Embedded interface - collect the type name using type info
266
+ if tv, ok := c.pkg.TypesInfo.Types[method.Type]; ok && tv.Type != nil {
267
+ if namedType, ok := tv.Type.(*gtypes.Named); ok {
268
+ embeddedInterfaces = append(embeddedInterfaces, namedType.Obj().Name())
269
+ } else {
270
+ c.tsw.WriteCommentLine(fmt.Sprintf("// Unhandled embedded interface type: %T", tv.Type))
271
+ }
272
+ } else {
273
+ c.tsw.WriteCommentLine("// Could not resolve embedded interface type")
274
+ }
226
275
  }
227
- c.tsw.WriteLine(";")
228
- } else {
229
- // Embedded interface - write the type name
230
- c.WriteTypeExpr(method.Type)
231
- c.tsw.WriteLine("; // Embedded interface - requires manual merging or mixin in TS")
232
276
  }
233
277
  }
278
+
279
+ // If there are embedded interfaces, write the extends clause
280
+ if len(embeddedInterfaces) > 0 {
281
+ c.tsw.WriteLiterally(" extends ")
282
+ c.tsw.WriteLiterally(strings.Join(embeddedInterfaces, ", "))
283
+ }
284
+
285
+ // Write the interface body on the same line
286
+ c.tsw.WriteLiterally("{") // Removed leading space
287
+ c.tsw.WriteLine("") // Newline after opening brace
288
+ c.tsw.Indent(1)
289
+
290
+ // Write named methods
291
+ for _, method := range methods {
292
+ if err := c.WriteInterfaceMethodSignature(method); err != nil {
293
+ c.tsw.WriteCommentInline(fmt.Sprintf("error writing interface method signature: %v", err))
294
+ }
295
+ }
296
+
234
297
  c.tsw.Indent(-1)
235
298
  c.tsw.WriteLine("}")
236
299
  }
@@ -790,7 +853,38 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
790
853
  // Write arrow function: (params) => { body }
791
854
  c.tsw.WriteLiterally("(")
792
855
  c.WriteFieldList(exp.Type.Params, true) // true = arguments
793
- c.tsw.WriteLiterally(") => ")
856
+ c.tsw.WriteLiterally(")")
857
+
858
+ // Handle return type for function literals
859
+ if exp.Type.Results != nil && len(exp.Type.Results.List) > 0 {
860
+ c.tsw.WriteLiterally(": ")
861
+ if isAsync {
862
+ c.tsw.WriteLiterally("Promise<")
863
+ }
864
+ if len(exp.Type.Results.List) == 1 && len(exp.Type.Results.List[0].Names) == 0 {
865
+ c.WriteTypeExpr(exp.Type.Results.List[0].Type)
866
+ } else {
867
+ c.tsw.WriteLiterally("[")
868
+ for i, field := range exp.Type.Results.List {
869
+ if i > 0 {
870
+ c.tsw.WriteLiterally(", ")
871
+ }
872
+ c.WriteTypeExpr(field.Type)
873
+ }
874
+ c.tsw.WriteLiterally("]")
875
+ }
876
+ if isAsync {
877
+ c.tsw.WriteLiterally(">")
878
+ }
879
+ } else {
880
+ if isAsync {
881
+ c.tsw.WriteLiterally(": Promise<void>")
882
+ } else {
883
+ c.tsw.WriteLiterally(": void")
884
+ }
885
+ }
886
+
887
+ c.tsw.WriteLiterally(" => ")
794
888
 
795
889
  // Write function body
796
890
  if err := c.WriteStmt(exp.Body); err != nil {
@@ -3,6 +3,9 @@ package compiler
3
3
  import (
4
4
  "fmt"
5
5
  "go/ast"
6
+ "go/types"
7
+ "sort"
8
+ "strings"
6
9
  )
7
10
 
8
11
  // WriteSpec writes a specification to the output.
@@ -24,6 +27,88 @@ func (c *GoToTSCompiler) WriteSpec(a ast.Spec) error {
24
27
  return nil
25
28
  }
26
29
 
30
+ // collectMethodNames returns a comma-separated string of method names for a struct
31
+ func (c *GoToTSCompiler) collectMethodNames(structName string) string {
32
+ var methodNames []string
33
+
34
+ for _, fileSyntax := range c.pkg.Syntax {
35
+ for _, decl := range fileSyntax.Decls {
36
+ funcDecl, isFunc := decl.(*ast.FuncDecl)
37
+ if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
38
+ continue // Skip non-functions or functions without receivers
39
+ }
40
+
41
+ // Check if the receiver type matches the struct name
42
+ recvField := funcDecl.Recv.List[0]
43
+ recvType := recvField.Type
44
+ // Handle pointer receivers (*MyStruct) and value receivers (MyStruct)
45
+ if starExpr, ok := recvType.(*ast.StarExpr); ok {
46
+ recvType = starExpr.X // Get the type being pointed to
47
+ }
48
+
49
+ // Check if the receiver identifier name matches the struct name
50
+ if ident, ok := recvType.(*ast.Ident); ok && ident.Name == structName {
51
+ // Found a method for this struct
52
+ methodNames = append(methodNames, fmt.Sprintf("'%s'", funcDecl.Name.Name))
53
+ }
54
+ }
55
+ }
56
+
57
+ return strings.Join(methodNames, ", ")
58
+ }
59
+
60
+ // collectInterfaceMethods returns a comma-separated string of method names for an interface
61
+ func (c *GoToTSCompiler) collectInterfaceMethods(interfaceType *ast.InterfaceType) string {
62
+ // Use a map to ensure uniqueness of method names
63
+ methodNamesMap := make(map[string]struct{})
64
+
65
+ if interfaceType.Methods != nil {
66
+ for _, method := range interfaceType.Methods.List {
67
+ if len(method.Names) > 0 {
68
+ // Named method
69
+ methodNamesMap[method.Names[0].Name] = struct{}{}
70
+ } else {
71
+ // Embedded interface - resolve it and collect its methods
72
+ embeddedType := method.Type
73
+
74
+ // Resolve the embedded interface using type information
75
+ if tv, ok := c.pkg.TypesInfo.Types[embeddedType]; ok && tv.Type != nil {
76
+ // Get the underlying interface type
77
+ var ifaceType *types.Interface
78
+ if named, ok := tv.Type.(*types.Named); ok {
79
+ if underlying, ok := named.Underlying().(*types.Interface); ok {
80
+ ifaceType = underlying
81
+ }
82
+ } else if underlying, ok := tv.Type.(*types.Interface); ok {
83
+ ifaceType = underlying
84
+ }
85
+
86
+ // Collect methods from the interface
87
+ if ifaceType != nil {
88
+ for i := 0; i < ifaceType.NumMethods(); i++ {
89
+ methodNamesMap[ifaceType.Method(i).Name()] = struct{}{}
90
+ }
91
+ }
92
+ } else {
93
+ // Couldn't resolve the embedded interface
94
+ c.tsw.WriteCommentLine("// Note: Some embedded interface methods may not be fully resolved")
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ // Convert the map to a slice for a deterministic output order
101
+ var methodNames []string
102
+ for name := range methodNamesMap {
103
+ methodNames = append(methodNames, fmt.Sprintf("'%s'", name))
104
+ }
105
+
106
+ // Sort for deterministic output
107
+ sort.Strings(methodNames)
108
+
109
+ return strings.Join(methodNames, ", ")
110
+ }
111
+
27
112
  // WriteTypeSpec writes the type specification to the output.
28
113
  func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
29
114
  if a.Doc != nil {
@@ -81,6 +166,18 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
81
166
  c.tsw.WriteLine("")
82
167
  c.tsw.WriteLinef("constructor(init?: Partial<%s>) { if (init) Object.assign(this, init as any); }", className)
83
168
  c.tsw.WriteLinef("public clone(): %s { return Object.assign(Object.create(%s.prototype) as %s, this); }", className, className, className)
169
+
170
+ // Add code to register the type with the runtime system
171
+ c.tsw.WriteLine("")
172
+ c.tsw.WriteLinef("// Register this type with the runtime type system")
173
+ c.tsw.WriteLinef("static __typeInfo = goscript.registerType(")
174
+ c.tsw.WriteLinef(" '%s',", className)
175
+ c.tsw.WriteLinef(" goscript.TypeKind.Struct,")
176
+ c.tsw.WriteLinef(" new %s(),", className)
177
+ c.tsw.WriteLinef(" new Set([%s]),", c.collectMethodNames(className))
178
+ c.tsw.WriteLinef(" %s", className)
179
+ c.tsw.WriteLinef(");")
180
+
84
181
  c.tsw.Indent(-1)
85
182
  c.tsw.WriteLine("}")
86
183
  case *ast.InterfaceType:
@@ -88,8 +185,20 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
88
185
  if err := c.WriteValueExpr(a.Name); err != nil { // Interface name is a value identifier
89
186
  return err
90
187
  }
91
- c.tsw.WriteLine(" ")
92
- c.WriteTypeExpr(a.Type) // The interface definition itself is a type
188
+ c.tsw.WriteLiterally(" ") // Changed from WriteLine to WriteLiterally
189
+ c.WriteTypeExpr(a.Type) // The interface definition itself is a type
190
+
191
+ // Add code to register the interface with the runtime system
192
+ interfaceName := a.Name.Name
193
+ c.tsw.WriteLine("")
194
+ c.tsw.WriteLinef("// Register this interface with the runtime type system")
195
+ c.tsw.WriteLinef("const %s__typeInfo = goscript.registerType(", interfaceName)
196
+ c.tsw.WriteLinef(" '%s',", interfaceName)
197
+ c.tsw.WriteLinef(" goscript.TypeKind.Interface,")
198
+ c.tsw.WriteLinef(" null,") // Zero value for interface is null
199
+ c.tsw.WriteLinef(" new Set([%s]),", c.collectInterfaceMethods(t))
200
+ c.tsw.WriteLinef(" undefined")
201
+ c.tsw.WriteLinef(");")
93
202
  default:
94
203
  // type alias
95
204
  c.tsw.WriteLiterally("type ")
@@ -254,3 +363,67 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
254
363
 
255
364
  c.tsw.WriteImport(impName, importPath)
256
365
  }
366
+
367
+ // WriteInterfaceMethodSignature writes a TypeScript interface method signature from a Go ast.Field.
368
+ func (c *GoToTSCompiler) WriteInterfaceMethodSignature(field *ast.Field) error {
369
+ if len(field.Names) == 0 {
370
+ // Should not happen for named methods in an interface, but handle defensively
371
+ return fmt.Errorf("interface method field has no name")
372
+ }
373
+
374
+ methodName := field.Names[0]
375
+ funcType, ok := field.Type.(*ast.FuncType)
376
+ if !ok {
377
+ // Should not happen for valid interface methods, but handle defensively
378
+ c.tsw.WriteCommentInline("unexpected interface method type")
379
+ return fmt.Errorf("interface method type is not a FuncType")
380
+ }
381
+
382
+ // Write method name
383
+ c.WriteIdentValue(methodName)
384
+
385
+ // Write parameter list (name: type)
386
+ c.tsw.WriteLiterally("(")
387
+ if funcType.Params != nil {
388
+ for i, param := range funcType.Params.List {
389
+ if i > 0 {
390
+ c.tsw.WriteLiterally(", ")
391
+ }
392
+ // Determine parameter name
393
+ paramName := fmt.Sprintf("_p%d", i) // Default placeholder
394
+ if len(param.Names) > 0 && param.Names[0].Name != "" && param.Names[0].Name != "_" {
395
+ paramName = param.Names[0].Name
396
+ }
397
+ c.tsw.WriteLiterally(paramName)
398
+ c.tsw.WriteLiterally(": ")
399
+ c.WriteTypeExpr(param.Type)
400
+ }
401
+ }
402
+ c.tsw.WriteLiterally(")")
403
+
404
+ // Write return type
405
+ // Use WriteFuncType's logic for return types, but without the async handling
406
+ if funcType.Results != nil && len(funcType.Results.List) > 0 {
407
+ c.tsw.WriteLiterally(": ")
408
+ if len(funcType.Results.List) == 1 && len(funcType.Results.List[0].Names) == 0 {
409
+ // Single unnamed return type
410
+ c.WriteTypeExpr(funcType.Results.List[0].Type)
411
+ } else {
412
+ // Multiple or named return types -> tuple
413
+ c.tsw.WriteLiterally("[")
414
+ for i, result := range funcType.Results.List {
415
+ if i > 0 {
416
+ c.tsw.WriteLiterally(", ")
417
+ }
418
+ c.WriteTypeExpr(result.Type)
419
+ }
420
+ c.tsw.WriteLiterally("]")
421
+ }
422
+ } else {
423
+ // No return value -> void
424
+ c.tsw.WriteLiterally(": void")
425
+ }
426
+
427
+ c.tsw.WriteLine(";") // Semicolon at the end of the method signature
428
+ return nil
429
+ }