goscript 0.0.21 → 0.0.23
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/cmd/goscript/cmd_compile.go +2 -2
- package/compiler/analysis.go +229 -51
- package/compiler/assignment.go +412 -0
- package/compiler/compiler.go +185 -5885
- package/compiler/compiler_test.go +40 -8
- package/compiler/composite-lit.go +552 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +259 -0
- package/compiler/expr-call.go +479 -0
- package/compiler/expr-selector.go +125 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +309 -0
- package/compiler/expr-value.go +89 -0
- package/compiler/expr.go +591 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +131 -0
- package/compiler/primitive.go +148 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +211 -204
- package/compiler/spec-value.go +226 -0
- package/compiler/spec.go +272 -0
- package/compiler/stmt-assign.go +439 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +235 -0
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +792 -0
- package/compiler/type-assert.go +209 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +618 -0
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +6 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2114
- package/dist/builtin/builtin.d.ts +0 -495
- package/dist/builtin/builtin.js +0 -1490
- package/dist/builtin/builtin.js.map +0 -1
- /package/compiler/{writer.go → code-writer.go} +0 -0
package/compiler/type.go
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/types"
|
|
7
|
+
"strings"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// GoTypeContext specifies the context in which a Go type is being translated to TypeScript.
|
|
11
|
+
// This affects how certain types (especially pointers) are handled.
|
|
12
|
+
type GoTypeContext int
|
|
13
|
+
|
|
14
|
+
const (
|
|
15
|
+
// GoTypeContextGeneral is used for general type translation
|
|
16
|
+
GoTypeContextGeneral GoTypeContext = iota
|
|
17
|
+
// GoTypeContextFunctionReturn is used when translating types for function return values.
|
|
18
|
+
// In this context, pointer-to-struct types become `ClassName | null` instead of
|
|
19
|
+
// `$.Box<ClassName> | null` because function return values cannot be addressed.
|
|
20
|
+
GoTypeContextFunctionReturn
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// WriteGoType is the main dispatcher for translating Go types to their TypeScript
|
|
24
|
+
// equivalents. It examines the type and delegates to more specialized type writer
|
|
25
|
+
// functions based on the specific Go type encountered.
|
|
26
|
+
//
|
|
27
|
+
// The context parameter controls how certain types (especially pointers) are handled:
|
|
28
|
+
// - GoTypeContextGeneral: Standard type translation
|
|
29
|
+
// - GoTypeContextFunctionReturn: Special handling for function return types where
|
|
30
|
+
// pointer-to-struct types become `ClassName | null` instead of `$.Box<ClassName> | null`
|
|
31
|
+
//
|
|
32
|
+
// It handles nil types as 'any' with a comment, and dispatches to appropriate
|
|
33
|
+
// type-specific writers for all other recognized Go types.
|
|
34
|
+
func (c *GoToTSCompiler) WriteGoType(typ types.Type, context GoTypeContext) {
|
|
35
|
+
if typ == nil {
|
|
36
|
+
c.tsw.WriteLiterally("any")
|
|
37
|
+
c.tsw.WriteCommentInline("nil type")
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
switch t := typ.(type) {
|
|
42
|
+
case *types.Basic:
|
|
43
|
+
c.WriteBasicType(t)
|
|
44
|
+
case *types.Named:
|
|
45
|
+
c.WriteNamedType(t)
|
|
46
|
+
case *types.Pointer:
|
|
47
|
+
if context == GoTypeContextFunctionReturn {
|
|
48
|
+
c.writePointerTypeForFunctionReturn(t)
|
|
49
|
+
} else {
|
|
50
|
+
c.WritePointerType(t)
|
|
51
|
+
}
|
|
52
|
+
case *types.Slice:
|
|
53
|
+
c.WriteSliceType(t)
|
|
54
|
+
case *types.Array:
|
|
55
|
+
c.WriteArrayType(t)
|
|
56
|
+
case *types.Map:
|
|
57
|
+
c.WriteMapType(t)
|
|
58
|
+
case *types.Chan:
|
|
59
|
+
c.WriteChannelType(t)
|
|
60
|
+
case *types.Interface:
|
|
61
|
+
c.WriteInterfaceType(t, nil) // No ast.InterfaceType available here
|
|
62
|
+
case *types.Signature:
|
|
63
|
+
c.WriteSignatureType(t)
|
|
64
|
+
case *types.Struct:
|
|
65
|
+
c.WriteStructType(t)
|
|
66
|
+
case *types.Alias:
|
|
67
|
+
c.WriteGoType(t.Underlying(), context)
|
|
68
|
+
case *types.TypeParam:
|
|
69
|
+
// For type parameters, write the type parameter name (e.g., "T", "K", etc.)
|
|
70
|
+
c.tsw.WriteLiterally(t.Obj().Name())
|
|
71
|
+
default:
|
|
72
|
+
// For other types, just write "any" and add a comment
|
|
73
|
+
c.tsw.WriteLiterally("any")
|
|
74
|
+
c.tsw.WriteCommentInlinef("unhandled type: %T", typ)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// writePointerTypeForFunctionReturn translates a Go pointer type (*T) to its TypeScript
|
|
79
|
+
// equivalent for function return types. Unlike WritePointerType, this function
|
|
80
|
+
// handles pointer-to-struct types specially: they become `ClassName | null` instead
|
|
81
|
+
// of `$.Box<ClassName> | null` because function return values cannot be addressed.
|
|
82
|
+
func (c *GoToTSCompiler) writePointerTypeForFunctionReturn(t *types.Pointer) {
|
|
83
|
+
elemType := t.Elem()
|
|
84
|
+
|
|
85
|
+
// Check if the element type is a struct (directly or via a named type)
|
|
86
|
+
isStructType := false
|
|
87
|
+
if _, ok := elemType.Underlying().(*types.Struct); ok {
|
|
88
|
+
isStructType = true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if isStructType {
|
|
92
|
+
// For pointer-to-struct in function returns, generate ClassName | null
|
|
93
|
+
c.WriteGoType(elemType, GoTypeContextFunctionReturn)
|
|
94
|
+
c.tsw.WriteLiterally(" | null")
|
|
95
|
+
} else {
|
|
96
|
+
// For pointer-to-primitive in function returns, still use boxing
|
|
97
|
+
c.tsw.WriteLiterally("$.Box<")
|
|
98
|
+
c.WriteGoType(elemType, GoTypeContextFunctionReturn)
|
|
99
|
+
c.tsw.WriteLiterally("> | null")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// WriteZeroValueForType writes the TypeScript representation of the zero value
|
|
104
|
+
// for a given Go type.
|
|
105
|
+
// It handles `types.Array` by recursively writing zero values for each element
|
|
106
|
+
// to form a TypeScript array literal (e.g., `[0, 0, 0]`).
|
|
107
|
+
// For `types.Basic` (like `bool`, `string`, numeric types), it writes the
|
|
108
|
+
// corresponding TypeScript zero value (`false`, `""`, `0`).
|
|
109
|
+
// For `[]byte`, it writes `new Uint8Array(0)`.
|
|
110
|
+
// Other types default to `null`. This function is primarily used for initializing
|
|
111
|
+
// arrays and variables where an explicit initializer is absent.
|
|
112
|
+
func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
|
|
113
|
+
switch t := typ.(type) {
|
|
114
|
+
case *types.Array:
|
|
115
|
+
c.tsw.WriteLiterally("[")
|
|
116
|
+
for i := 0; i < int(t.Len()); i++ {
|
|
117
|
+
if i > 0 {
|
|
118
|
+
c.tsw.WriteLiterally(", ")
|
|
119
|
+
}
|
|
120
|
+
c.WriteZeroValueForType(t.Elem())
|
|
121
|
+
}
|
|
122
|
+
c.tsw.WriteLiterally("]")
|
|
123
|
+
case *ast.Expr:
|
|
124
|
+
// For AST expressions, get the type and handle that instead
|
|
125
|
+
if expr := *t; expr != nil {
|
|
126
|
+
if typ := c.pkg.TypesInfo.TypeOf(expr); typ != nil {
|
|
127
|
+
c.WriteZeroValueForType(typ)
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
c.tsw.WriteLiterally("null")
|
|
132
|
+
case *types.Basic:
|
|
133
|
+
switch t.Kind() {
|
|
134
|
+
case types.Bool:
|
|
135
|
+
c.tsw.WriteLiterally("false")
|
|
136
|
+
case types.String:
|
|
137
|
+
c.tsw.WriteLiterally(`""`)
|
|
138
|
+
default:
|
|
139
|
+
c.tsw.WriteLiterally("0")
|
|
140
|
+
}
|
|
141
|
+
case *types.Named:
|
|
142
|
+
// Handle named types, especially struct types
|
|
143
|
+
if _, isStruct := t.Underlying().(*types.Struct); isStruct {
|
|
144
|
+
// Initialize struct types with a new instance
|
|
145
|
+
c.tsw.WriteLiterallyf("new %s()", t.Obj().Name())
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
// For other named types, use the zero value of the underlying type
|
|
149
|
+
c.WriteZeroValueForType(t.Underlying())
|
|
150
|
+
case *types.Slice:
|
|
151
|
+
// Check if it's a []byte slice
|
|
152
|
+
if elem, ok := t.Elem().(*types.Basic); ok && elem.Kind() == types.Uint8 {
|
|
153
|
+
c.tsw.WriteLiterally("new Uint8Array(0)")
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
// For other slice types, default to null
|
|
157
|
+
c.tsw.WriteLiterally("null")
|
|
158
|
+
case *types.Struct:
|
|
159
|
+
// For anonymous struct types, initialize with {}
|
|
160
|
+
c.tsw.WriteLiterally("{}")
|
|
161
|
+
case *types.TypeParam:
|
|
162
|
+
// For type parameters, use null! (non-null assertion) to avoid TypeScript
|
|
163
|
+
// casting errors with union types like string | Uint8Array
|
|
164
|
+
c.tsw.WriteLiterally("null!")
|
|
165
|
+
default:
|
|
166
|
+
c.tsw.WriteLiterally("null")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// WriteBasicType translates a Go basic type (primitives like int, string, bool)
|
|
171
|
+
// to its TypeScript equivalent.
|
|
172
|
+
// It handles untyped constants by mapping them to appropriate TypeScript types
|
|
173
|
+
// (boolean, number, string, null) and uses GoBuiltinToTypescript for typed primitives.
|
|
174
|
+
func (c *GoToTSCompiler) WriteBasicType(t *types.Basic) {
|
|
175
|
+
name := t.Name()
|
|
176
|
+
|
|
177
|
+
// Handle untyped constants by mapping them to appropriate TypeScript types
|
|
178
|
+
if t.Info()&types.IsUntyped != 0 {
|
|
179
|
+
switch t.Kind() {
|
|
180
|
+
case types.UntypedBool:
|
|
181
|
+
c.tsw.WriteLiterally("boolean")
|
|
182
|
+
return
|
|
183
|
+
case types.UntypedInt, types.UntypedFloat, types.UntypedComplex, types.UntypedRune:
|
|
184
|
+
c.tsw.WriteLiterally("number")
|
|
185
|
+
return
|
|
186
|
+
case types.UntypedString:
|
|
187
|
+
c.tsw.WriteLiterally("string")
|
|
188
|
+
return
|
|
189
|
+
case types.UntypedNil:
|
|
190
|
+
c.tsw.WriteLiterally("null")
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// For typed basic types, use the existing mapping
|
|
196
|
+
if tsType, ok := GoBuiltinToTypescript(name); ok {
|
|
197
|
+
c.tsw.WriteLiterally(tsType)
|
|
198
|
+
} else {
|
|
199
|
+
c.tsw.WriteLiterally(name)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// WriteNamedType translates a Go named type to its TypeScript equivalent.
|
|
204
|
+
// It specially handles the error interface as $.GoError, and uses the original
|
|
205
|
+
// type name for other named types. For generic types, it includes type arguments.
|
|
206
|
+
func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
|
|
207
|
+
// Check if the named type is the error interface
|
|
208
|
+
if iface, ok := t.Underlying().(*types.Interface); ok && iface.String() == "interface{Error() string}" {
|
|
209
|
+
c.tsw.WriteLiterally("$.GoError")
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Use Obj().Name() for the original defined name
|
|
214
|
+
c.tsw.WriteLiterally(t.Obj().Name())
|
|
215
|
+
|
|
216
|
+
// For generic types, include type arguments
|
|
217
|
+
if t.TypeArgs() != nil && t.TypeArgs().Len() > 0 {
|
|
218
|
+
c.tsw.WriteLiterally("<")
|
|
219
|
+
for i := 0; i < t.TypeArgs().Len(); i++ {
|
|
220
|
+
if i > 0 {
|
|
221
|
+
c.tsw.WriteLiterally(", ")
|
|
222
|
+
}
|
|
223
|
+
c.WriteGoType(t.TypeArgs().At(i), GoTypeContextGeneral)
|
|
224
|
+
}
|
|
225
|
+
c.tsw.WriteLiterally(">")
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// WritePointerType translates a Go pointer type (*T) to its TypeScript equivalent.
|
|
230
|
+
// It generates $.Box<T_ts> | null, where T_ts is the translated element type.
|
|
231
|
+
func (c *GoToTSCompiler) WritePointerType(t *types.Pointer) {
|
|
232
|
+
c.tsw.WriteLiterally("$.Box<")
|
|
233
|
+
c.WriteGoType(t.Elem(), GoTypeContextGeneral)
|
|
234
|
+
c.tsw.WriteLiterally("> | null") // Pointers are always nullable
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// WriteSliceType translates a Go slice type ([]T) to its TypeScript equivalent.
|
|
238
|
+
// It generates $.Slice<T_ts>, where T_ts is the translated element type.
|
|
239
|
+
// For []byte, it generates Uint8Array.
|
|
240
|
+
func (c *GoToTSCompiler) WriteSliceType(t *types.Slice) {
|
|
241
|
+
// Check if it's a []byte slice
|
|
242
|
+
if elem, ok := t.Elem().(*types.Basic); ok && elem.Kind() == types.Uint8 {
|
|
243
|
+
c.tsw.WriteLiterally("Uint8Array")
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
c.tsw.WriteLiterally("$.Slice<")
|
|
247
|
+
c.WriteGoType(t.Elem(), GoTypeContextGeneral)
|
|
248
|
+
c.tsw.WriteLiterally(">")
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// WriteArrayType translates a Go array type ([N]T) to its TypeScript equivalent.
|
|
252
|
+
// It generates T_ts[], where T_ts is the translated element type.
|
|
253
|
+
func (c *GoToTSCompiler) WriteArrayType(t *types.Array) {
|
|
254
|
+
c.WriteGoType(t.Elem(), GoTypeContextGeneral)
|
|
255
|
+
c.tsw.WriteLiterally("[]") // Arrays cannot be nil
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// WriteMapType translates a Go map type (map[K]V) to its TypeScript equivalent.
|
|
259
|
+
// It generates Map<K_ts, V_ts>, where K_ts and V_ts are the translated key
|
|
260
|
+
// and element types respectively.
|
|
261
|
+
func (c *GoToTSCompiler) WriteMapType(t *types.Map) {
|
|
262
|
+
c.tsw.WriteLiterally("Map<")
|
|
263
|
+
c.WriteGoType(t.Key(), GoTypeContextGeneral)
|
|
264
|
+
c.tsw.WriteLiterally(", ")
|
|
265
|
+
c.WriteGoType(t.Elem(), GoTypeContextGeneral)
|
|
266
|
+
c.tsw.WriteLiterally(">")
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// WriteChannelType translates a Go channel type (chan T) to its TypeScript equivalent.
|
|
270
|
+
// It generates $.Channel<T_ts> | null, where T_ts is the translated element type.
|
|
271
|
+
// Channels are nilable in Go, so they are represented as nullable types in TypeScript.
|
|
272
|
+
func (c *GoToTSCompiler) WriteChannelType(t *types.Chan) {
|
|
273
|
+
c.tsw.WriteLiterally("$.Channel<")
|
|
274
|
+
c.WriteGoType(t.Elem(), GoTypeContextGeneral)
|
|
275
|
+
c.tsw.WriteLiterally("> | null")
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// WriteFuncType translates a Go function type (`ast.FuncType`) into a TypeScript
|
|
279
|
+
// function signature.
|
|
280
|
+
// The signature is of the form `(param1: type1, param2: type2) => returnType`.
|
|
281
|
+
// - Parameters are written using `WriteFieldList`.
|
|
282
|
+
// - Return types:
|
|
283
|
+
// - If there are no results, the return type is `void`.
|
|
284
|
+
// - If there's a single, unnamed result, it's `resultType`.
|
|
285
|
+
// - If there are multiple or named results, it's a tuple type `[typeA, typeB]`.
|
|
286
|
+
// - If `isAsync` is true (indicating the function is known to perform async
|
|
287
|
+
// operations like channel interactions or contains `go` or `defer` with async calls),
|
|
288
|
+
// the return type is wrapped in `Promise<>` (e.g., `Promise<void>`, `Promise<number>`).
|
|
289
|
+
func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
|
|
290
|
+
c.tsw.WriteLiterally("(")
|
|
291
|
+
c.WriteFieldList(exp.Params, true) // true = arguments
|
|
292
|
+
c.tsw.WriteLiterally(")")
|
|
293
|
+
if exp.Results != nil && len(exp.Results.List) > 0 {
|
|
294
|
+
// Use colon for return type annotation
|
|
295
|
+
c.tsw.WriteLiterally(": ")
|
|
296
|
+
if isAsync {
|
|
297
|
+
c.tsw.WriteLiterally("Promise<")
|
|
298
|
+
}
|
|
299
|
+
if len(exp.Results.List) == 1 && len(exp.Results.List[0].Names) == 0 {
|
|
300
|
+
// Single unnamed return type
|
|
301
|
+
typ := c.pkg.TypesInfo.TypeOf(exp.Results.List[0].Type)
|
|
302
|
+
c.WriteGoType(typ, GoTypeContextFunctionReturn)
|
|
303
|
+
} else {
|
|
304
|
+
// Multiple or named return types -> tuple
|
|
305
|
+
c.tsw.WriteLiterally("[")
|
|
306
|
+
for i, field := range exp.Results.List {
|
|
307
|
+
if i > 0 {
|
|
308
|
+
c.tsw.WriteLiterally(", ")
|
|
309
|
+
}
|
|
310
|
+
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
311
|
+
c.WriteGoType(typ, GoTypeContextFunctionReturn)
|
|
312
|
+
}
|
|
313
|
+
c.tsw.WriteLiterally("]")
|
|
314
|
+
}
|
|
315
|
+
if isAsync {
|
|
316
|
+
c.tsw.WriteLiterally(">")
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
// No return value -> void
|
|
320
|
+
if isAsync {
|
|
321
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
322
|
+
} else {
|
|
323
|
+
c.tsw.WriteLiterally(": void")
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// WriteInterfaceType translates a Go interface type to its TypeScript equivalent.
|
|
329
|
+
// It specially handles the error interface as $.GoError, and delegates to
|
|
330
|
+
// writeInterfaceStructure for other interface types, prepending "null | ".
|
|
331
|
+
// If astNode is provided (e.g., from a type spec), comments for methods will be included.
|
|
332
|
+
func (c *GoToTSCompiler) WriteInterfaceType(t *types.Interface, astNode *ast.InterfaceType) {
|
|
333
|
+
// Handle the built-in error interface specifically
|
|
334
|
+
if t.String() == "interface{Error() string}" {
|
|
335
|
+
c.tsw.WriteLiterally("$.GoError")
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Prepend "null | " for all other interfaces.
|
|
340
|
+
// writeInterfaceStructure will handle the actual structure like "{...}" or "any".
|
|
341
|
+
c.tsw.WriteLiterally("null | ")
|
|
342
|
+
c.writeInterfaceStructure(t, astNode)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// WriteSignatureType translates a Go function signature to its TypeScript equivalent.
|
|
346
|
+
// It generates (param1: type1, param2: type2, ...): returnType for function types.
|
|
347
|
+
func (c *GoToTSCompiler) WriteSignatureType(t *types.Signature) {
|
|
348
|
+
c.tsw.WriteLiterally("(")
|
|
349
|
+
c.tsw.WriteLiterally("(")
|
|
350
|
+
params := t.Params()
|
|
351
|
+
for i := 0; i < params.Len(); i++ {
|
|
352
|
+
if i > 0 {
|
|
353
|
+
c.tsw.WriteLiterally(", ")
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
param := params.At(i)
|
|
357
|
+
paramSlice, paramIsSlice := param.Type().(*types.Slice)
|
|
358
|
+
|
|
359
|
+
paramVariadic := i == params.Len()-1 && t.Variadic()
|
|
360
|
+
if paramVariadic {
|
|
361
|
+
c.tsw.WriteLiterally("...")
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Use parameter name if available, otherwise use p0, p1, etc.
|
|
365
|
+
if param.Name() != "" {
|
|
366
|
+
c.tsw.WriteLiterally(param.Name())
|
|
367
|
+
} else {
|
|
368
|
+
c.tsw.WriteLiterallyf("p%d", i)
|
|
369
|
+
}
|
|
370
|
+
c.tsw.WriteLiterally(": ")
|
|
371
|
+
|
|
372
|
+
if paramVariadic && paramIsSlice {
|
|
373
|
+
c.WriteGoType(paramSlice.Elem(), GoTypeContextGeneral)
|
|
374
|
+
c.tsw.WriteLiterally("[]")
|
|
375
|
+
} else {
|
|
376
|
+
c.WriteGoType(param.Type(), GoTypeContextGeneral)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
c.tsw.WriteLiterally(")")
|
|
380
|
+
|
|
381
|
+
// Handle return types
|
|
382
|
+
c.tsw.WriteLiterally(" => ")
|
|
383
|
+
results := t.Results()
|
|
384
|
+
if results.Len() == 0 {
|
|
385
|
+
c.tsw.WriteLiterally("void")
|
|
386
|
+
} else if results.Len() == 1 {
|
|
387
|
+
c.WriteGoType(results.At(0).Type(), GoTypeContextFunctionReturn)
|
|
388
|
+
} else {
|
|
389
|
+
// Multiple return values -> tuple
|
|
390
|
+
c.tsw.WriteLiterally("[")
|
|
391
|
+
for i := 0; i < results.Len(); i++ {
|
|
392
|
+
if i > 0 {
|
|
393
|
+
c.tsw.WriteLiterally(", ")
|
|
394
|
+
}
|
|
395
|
+
c.WriteGoType(results.At(i).Type(), GoTypeContextFunctionReturn)
|
|
396
|
+
}
|
|
397
|
+
c.tsw.WriteLiterally("]")
|
|
398
|
+
}
|
|
399
|
+
c.tsw.WriteLiterally(") | null")
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// writeInterfaceStructure translates a Go `types.Interface` into its TypeScript structural representation.
|
|
403
|
+
// If astNode is provided, it's used to fetch comments for methods.
|
|
404
|
+
// For example, an interface `interface { MethodA(x int) string; EmbeddedB }` might become
|
|
405
|
+
// `{ MethodA(_p0: number): string; } & B_ts`.
|
|
406
|
+
func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode *ast.InterfaceType) {
|
|
407
|
+
// Handle empty interface interface{}
|
|
408
|
+
if iface.NumExplicitMethods() == 0 && iface.NumEmbeddeds() == 0 {
|
|
409
|
+
c.tsw.WriteLiterally("any") // Matches current behavior for interface{}
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Keep track if we've written any part (methods or first embedded type)
|
|
414
|
+
// to correctly place " & " separators.
|
|
415
|
+
firstPartWritten := false
|
|
416
|
+
|
|
417
|
+
// Handle explicit methods
|
|
418
|
+
if iface.NumExplicitMethods() > 0 {
|
|
419
|
+
c.tsw.WriteLiterally("{") // Opening brace for the object type
|
|
420
|
+
c.tsw.Indent(1)
|
|
421
|
+
c.tsw.WriteLine("") // Newline after opening brace, before the first method
|
|
422
|
+
|
|
423
|
+
for i := 0; i < iface.NumExplicitMethods(); i++ {
|
|
424
|
+
method := iface.ExplicitMethod(i)
|
|
425
|
+
sig := method.Type().(*types.Signature)
|
|
426
|
+
|
|
427
|
+
// Find corresponding ast.Field for comments if astNode is available
|
|
428
|
+
var astField *ast.Field
|
|
429
|
+
if astNode != nil && astNode.Methods != nil {
|
|
430
|
+
for _, f := range astNode.Methods.List {
|
|
431
|
+
// Ensure the field is a named method (not an embedded interface)
|
|
432
|
+
if len(f.Names) > 0 && f.Names[0].Name == method.Name() {
|
|
433
|
+
astField = f
|
|
434
|
+
break
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Write comments if astField is found
|
|
440
|
+
if astField != nil {
|
|
441
|
+
if astField.Doc != nil {
|
|
442
|
+
c.WriteDoc(astField.Doc) // WriteDoc handles newlines
|
|
443
|
+
}
|
|
444
|
+
if astField.Comment != nil { // For trailing comments on the same line in Go AST
|
|
445
|
+
c.WriteDoc(astField.Comment)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
c.tsw.WriteLiterally(method.Name())
|
|
450
|
+
c.tsw.WriteLiterally("(") // Start params
|
|
451
|
+
params := sig.Params()
|
|
452
|
+
for j := 0; j < params.Len(); j++ {
|
|
453
|
+
if j > 0 {
|
|
454
|
+
c.tsw.WriteLiterally(", ")
|
|
455
|
+
}
|
|
456
|
+
paramVar := params.At(j)
|
|
457
|
+
paramName := paramVar.Name()
|
|
458
|
+
if paramName == "" || paramName == "_" {
|
|
459
|
+
paramName = fmt.Sprintf("_p%d", j)
|
|
460
|
+
}
|
|
461
|
+
c.tsw.WriteLiterally(paramName)
|
|
462
|
+
c.tsw.WriteLiterally(": ")
|
|
463
|
+
c.WriteGoType(paramVar.Type(), GoTypeContextGeneral) // Recursive call for param type
|
|
464
|
+
}
|
|
465
|
+
c.tsw.WriteLiterally(")") // End params
|
|
466
|
+
|
|
467
|
+
// Return type
|
|
468
|
+
c.tsw.WriteLiterally(": ")
|
|
469
|
+
results := sig.Results()
|
|
470
|
+
if results.Len() == 0 {
|
|
471
|
+
c.tsw.WriteLiterally("void")
|
|
472
|
+
} else if results.Len() == 1 {
|
|
473
|
+
c.WriteGoType(results.At(0).Type(), GoTypeContextFunctionReturn) // Recursive call for result type
|
|
474
|
+
} else {
|
|
475
|
+
c.tsw.WriteLiterally("[")
|
|
476
|
+
for j := 0; j < results.Len(); j++ {
|
|
477
|
+
if j > 0 {
|
|
478
|
+
c.tsw.WriteLiterally(", ")
|
|
479
|
+
}
|
|
480
|
+
c.WriteGoType(results.At(j).Type(), GoTypeContextFunctionReturn) // Recursive call for result type
|
|
481
|
+
}
|
|
482
|
+
c.tsw.WriteLiterally("]")
|
|
483
|
+
}
|
|
484
|
+
c.tsw.WriteLine("") // newline for each method
|
|
485
|
+
}
|
|
486
|
+
c.tsw.Indent(-1)
|
|
487
|
+
c.tsw.WriteLiterally("}") // Closing brace for the object type
|
|
488
|
+
firstPartWritten = true
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Handle embedded types
|
|
492
|
+
if iface.NumEmbeddeds() > 0 {
|
|
493
|
+
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
|
494
|
+
if firstPartWritten {
|
|
495
|
+
c.tsw.WriteLiterally(" & ")
|
|
496
|
+
} else {
|
|
497
|
+
// This is the first part being written (no explicit methods, only embedded)
|
|
498
|
+
firstPartWritten = true
|
|
499
|
+
}
|
|
500
|
+
embeddedType := iface.EmbeddedType(i)
|
|
501
|
+
// When WriteGoType encounters an interface, it will call WriteInterfaceType
|
|
502
|
+
// which will pass nil for astNode, so comments for deeply embedded interface literals
|
|
503
|
+
// might not be available unless they are named types.
|
|
504
|
+
c.WriteGoType(embeddedType, GoTypeContextGeneral)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// getTypeString is a utility function that converts a Go `types.Type` into its
|
|
510
|
+
// TypeScript type string representation. It achieves this by creating a temporary
|
|
511
|
+
// `GoToTSCompiler` and `TSCodeWriter` (writing to a `strings.Builder`) and then
|
|
512
|
+
// calling `WriteGoType` on the provided Go type. This allows reusing the main
|
|
513
|
+
// type translation logic to get a string representation of the TypeScript type.
|
|
514
|
+
func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
|
|
515
|
+
var typeStr strings.Builder
|
|
516
|
+
writer := NewTSCodeWriter(&typeStr)
|
|
517
|
+
tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
|
|
518
|
+
tempCompiler.WriteGoType(goType, GoTypeContextGeneral)
|
|
519
|
+
return typeStr.String()
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// WriteStructType translates a Go struct type definition (`ast.StructType`)
|
|
523
|
+
// into a TypeScript anonymous object type (e.g., `{ Field1: Type1; Field2: Type2 }`).
|
|
524
|
+
// If the struct has no fields, it writes `{}`. Otherwise, it delegates to
|
|
525
|
+
// `WriteFieldList` to generate the list of field definitions.
|
|
526
|
+
// Note: This is for anonymous struct type literals. Named struct types are usually
|
|
527
|
+
// handled as classes via `WriteTypeSpec`.
|
|
528
|
+
func (c *GoToTSCompiler) WriteStructType(t *types.Struct) {
|
|
529
|
+
// Generate an interface with the struct's fields
|
|
530
|
+
c.tsw.WriteLiterally("{ ")
|
|
531
|
+
// Add field properties to the interface
|
|
532
|
+
for i := range t.NumFields() {
|
|
533
|
+
field := t.Field(i)
|
|
534
|
+
if i > 0 {
|
|
535
|
+
c.tsw.WriteLiterally("; ")
|
|
536
|
+
}
|
|
537
|
+
c.tsw.WriteLiterally(field.Name() + "?: ")
|
|
538
|
+
c.WriteGoType(field.Type(), GoTypeContextGeneral)
|
|
539
|
+
}
|
|
540
|
+
c.tsw.WriteLiterally(" }")
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// WriteTypeParameters translates Go type parameters to TypeScript generic parameters.
|
|
544
|
+
// It handles the TypeParams field of ast.FuncDecl and ast.TypeSpec to generate
|
|
545
|
+
// TypeScript generic parameter lists like <T extends SomeConstraint, U extends OtherConstraint>.
|
|
546
|
+
func (c *GoToTSCompiler) WriteTypeParameters(typeParams *ast.FieldList) {
|
|
547
|
+
if typeParams == nil || len(typeParams.List) == 0 {
|
|
548
|
+
return
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
c.tsw.WriteLiterally("<")
|
|
552
|
+
for i, field := range typeParams.List {
|
|
553
|
+
if i > 0 {
|
|
554
|
+
c.tsw.WriteLiterally(", ")
|
|
555
|
+
}
|
|
556
|
+
// Write each type parameter name and constraint
|
|
557
|
+
for j, name := range field.Names {
|
|
558
|
+
if j > 0 {
|
|
559
|
+
c.tsw.WriteLiterally(", ")
|
|
560
|
+
}
|
|
561
|
+
c.tsw.WriteLiterally(name.Name)
|
|
562
|
+
|
|
563
|
+
// Write constraint if present
|
|
564
|
+
if field.Type != nil {
|
|
565
|
+
c.tsw.WriteLiterally(" extends ")
|
|
566
|
+
c.WriteTypeConstraint(field.Type)
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
c.tsw.WriteLiterally(">")
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// WriteTypeConstraint translates Go type constraints to TypeScript constraint expressions.
|
|
574
|
+
// It handles different constraint types including:
|
|
575
|
+
// - Union types: []byte | string -> string | Uint8Array
|
|
576
|
+
// - Interface types: interface{Method()} -> {Method(): void}
|
|
577
|
+
// - Basic types: any -> any, comparable -> $.Comparable
|
|
578
|
+
func (c *GoToTSCompiler) WriteTypeConstraint(constraint ast.Expr) {
|
|
579
|
+
switch t := constraint.(type) {
|
|
580
|
+
case *ast.Ident:
|
|
581
|
+
// Handle predeclared constraints
|
|
582
|
+
switch t.Name {
|
|
583
|
+
case "any":
|
|
584
|
+
c.tsw.WriteLiterally("any")
|
|
585
|
+
case "comparable":
|
|
586
|
+
c.tsw.WriteLiterally("$.Comparable")
|
|
587
|
+
default:
|
|
588
|
+
// Use the type directly
|
|
589
|
+
c.WriteTypeExpr(t)
|
|
590
|
+
}
|
|
591
|
+
case *ast.BinaryExpr:
|
|
592
|
+
// Handle union types like []byte | string
|
|
593
|
+
if t.Op.String() == "|" {
|
|
594
|
+
c.WriteTypeConstraint(t.X)
|
|
595
|
+
c.tsw.WriteLiterally(" | ")
|
|
596
|
+
c.WriteTypeConstraint(t.Y)
|
|
597
|
+
} else {
|
|
598
|
+
// Fallback for other binary expressions
|
|
599
|
+
c.WriteTypeExpr(constraint)
|
|
600
|
+
}
|
|
601
|
+
case *ast.InterfaceType:
|
|
602
|
+
// Handle interface constraints
|
|
603
|
+
c.WriteTypeExpr(constraint)
|
|
604
|
+
case *ast.ArrayType:
|
|
605
|
+
// Handle []byte specifically
|
|
606
|
+
if ident, ok := t.Elt.(*ast.Ident); ok && ident.Name == "byte" {
|
|
607
|
+
c.tsw.WriteLiterally("Uint8Array")
|
|
608
|
+
} else {
|
|
609
|
+
c.WriteTypeExpr(constraint)
|
|
610
|
+
}
|
|
611
|
+
case *ast.SliceExpr:
|
|
612
|
+
// Handle slice types in constraints
|
|
613
|
+
c.WriteTypeExpr(constraint)
|
|
614
|
+
default:
|
|
615
|
+
// Fallback: use the standard type expression writer
|
|
616
|
+
c.WriteTypeExpr(constraint)
|
|
617
|
+
}
|
|
618
|
+
}
|
package/go.mod
CHANGED
|
@@ -4,13 +4,14 @@ go 1.24.3
|
|
|
4
4
|
|
|
5
5
|
require (
|
|
6
6
|
github.com/aperturerobotics/cli v1.0.0
|
|
7
|
+
github.com/aperturerobotics/util v1.30.0
|
|
7
8
|
github.com/pkg/errors v0.9.1
|
|
8
9
|
github.com/sirupsen/logrus v1.9.3
|
|
9
10
|
golang.org/x/tools v0.33.0
|
|
10
11
|
)
|
|
11
12
|
|
|
12
13
|
require (
|
|
13
|
-
github.com/aperturerobotics/common v0.
|
|
14
|
+
github.com/aperturerobotics/common v0.22.1 // indirect
|
|
14
15
|
github.com/stretchr/testify v1.10.0 // indirect
|
|
15
16
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
|
16
17
|
golang.org/x/mod v0.24.0 // indirect
|
package/go.sum
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
github.com/aperturerobotics/cli v1.0.0 h1:s3xT2h7eBih4/4yZKTn/HQ6P+qpk6ygWZl2416xAI1M=
|
|
2
2
|
github.com/aperturerobotics/cli v1.0.0/go.mod h1:wtlINjMcKuwyV1x4ftReuA6hHZcPB8kPMXHyQqGFCSc=
|
|
3
|
-
github.com/aperturerobotics/common v0.
|
|
4
|
-
github.com/aperturerobotics/common v0.
|
|
3
|
+
github.com/aperturerobotics/common v0.22.1 h1:wxTV9wSgfAM9jYUuSzNFzUeC28DQMBgDO3iGahlHeaY=
|
|
4
|
+
github.com/aperturerobotics/common v0.22.1/go.mod h1:wsPfDVCTNpGHddg/MSfm84rKoO4GAvb+TQtATXz+pKY=
|
|
5
|
+
github.com/aperturerobotics/util v1.30.0 h1:OKhFVPnAfR8/dfVNV27EtMr27C0kzwPiStoCwKiint0=
|
|
6
|
+
github.com/aperturerobotics/util v1.30.0/go.mod h1:T97YTP+FVLegYo5rylOVaPuTLyZyiDqYxD5zVLI9YAc=
|
|
5
7
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
6
8
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
7
9
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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.
|
|
4
|
+
"version": "0.0.23",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Aperture Robotics LLC.",
|
|
7
7
|
"email": "support@aperture.us",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"default": "./dist/compiler/index.js"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"./builtin": {
|
|
37
|
+
"./gs/builtin": {
|
|
38
38
|
"import": {
|
|
39
|
-
"types": "./dist/builtin/builtin.d.ts",
|
|
40
|
-
"default": "./dist/builtin/builtin.js"
|
|
39
|
+
"types": "./dist/gs/builtin/builtin.d.ts",
|
|
40
|
+
"default": "./dist/gs/builtin/builtin.js"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
},
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"format": "npm run format:go && npm run format:js && npm run format:config",
|
|
53
53
|
"format:config": "prettier --write tsconfig.json package.json",
|
|
54
54
|
"format:go": "gofumpt -w .",
|
|
55
|
-
"format:js": "prettier --write './{src,
|
|
55
|
+
"format:js": "prettier --write './{src,gs,example}/**/(*.ts|*.tsx|*.html|*.css|*.scss)'",
|
|
56
56
|
"release": "npm run release:version && npm run release:commit",
|
|
57
57
|
"release:minor": "npm run release:version:minor && npm run release:commit",
|
|
58
58
|
"release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@eslint/js": "^9.25.1",
|
|
86
|
-
"@types/node": "^22.15.
|
|
86
|
+
"@types/node": "^22.15.18",
|
|
87
87
|
"@typescript-eslint/eslint-plugin": "^8.31.0",
|
|
88
88
|
"@typescript-eslint/parser": "^8.31.0",
|
|
89
89
|
"eslint": "^9.25.1",
|