goscript 0.0.21 → 0.0.22
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/builtin/builtin.ts +298 -33
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +160 -5883
- package/compiler/compiler_test.go +4 -0
- package/compiler/composite-lit.go +537 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +223 -0
- package/compiler/expr-call.go +423 -0
- package/compiler/expr-selector.go +105 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +182 -0
- package/compiler/expr-value.go +119 -0
- package/compiler/expr.go +356 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +99 -0
- package/compiler/primitive.go +142 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +79 -203
- package/compiler/spec-value.go +194 -0
- package/compiler/spec.go +263 -0
- package/compiler/stmt-assign.go +411 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +237 -0
- package/compiler/stmt.go +907 -0
- package/compiler/type-assert.go +463 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +556 -0
- package/dist/builtin/builtin.d.ts +24 -6
- package/dist/builtin/builtin.js +215 -19
- package/dist/builtin/builtin.js.map +1 -1
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +2 -2
- /package/compiler/{writer.go → code-writer.go} +0 -0
package/compiler/type.go
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/types"
|
|
7
|
+
"sort"
|
|
8
|
+
"strings"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// WriteGoType is the main dispatcher for translating Go types to their TypeScript
|
|
12
|
+
// equivalents. It examines the type and delegates to more specialized type writer
|
|
13
|
+
// functions based on the specific Go type encountered.
|
|
14
|
+
//
|
|
15
|
+
// It handles nil types as 'any' with a comment, and dispatches to appropriate
|
|
16
|
+
// type-specific writers for all other recognized Go types.
|
|
17
|
+
func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
|
|
18
|
+
if typ == nil {
|
|
19
|
+
c.tsw.WriteLiterally("any")
|
|
20
|
+
c.tsw.WriteCommentInline("nil type")
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
switch t := typ.(type) {
|
|
25
|
+
case *types.Basic:
|
|
26
|
+
c.WriteBasicType(t)
|
|
27
|
+
case *types.Named:
|
|
28
|
+
c.WriteNamedType(t)
|
|
29
|
+
case *types.Pointer:
|
|
30
|
+
c.WritePointerType(t)
|
|
31
|
+
case *types.Slice:
|
|
32
|
+
c.WriteSliceType(t)
|
|
33
|
+
case *types.Array:
|
|
34
|
+
c.WriteArrayType(t)
|
|
35
|
+
case *types.Map:
|
|
36
|
+
c.WriteMapType(t)
|
|
37
|
+
case *types.Chan:
|
|
38
|
+
c.WriteChannelType(t)
|
|
39
|
+
case *types.Interface:
|
|
40
|
+
c.WriteInterfaceType(t, nil) // No ast.InterfaceType available here
|
|
41
|
+
case *types.Signature:
|
|
42
|
+
c.WriteSignatureType(t)
|
|
43
|
+
case *types.Struct:
|
|
44
|
+
c.WriteStructType(t)
|
|
45
|
+
case *types.Alias:
|
|
46
|
+
c.WriteGoType(t.Underlying())
|
|
47
|
+
default:
|
|
48
|
+
// For other types, just write "any" and add a comment
|
|
49
|
+
c.tsw.WriteLiterally("any")
|
|
50
|
+
c.tsw.WriteCommentInlinef("unhandled type: %T", typ)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// WriteZeroValueForType writes the TypeScript representation of the zero value
|
|
55
|
+
// for a given Go type.
|
|
56
|
+
// It handles `types.Array` by recursively writing zero values for each element
|
|
57
|
+
// to form a TypeScript array literal (e.g., `[0, 0, 0]`).
|
|
58
|
+
// For `types.Basic` (like `bool`, `string`, numeric types), it writes the
|
|
59
|
+
// corresponding TypeScript zero value (`false`, `""`, `0`).
|
|
60
|
+
// Other types default to `null`. This function is primarily used for initializing
|
|
61
|
+
// arrays and variables where an explicit initializer is absent.
|
|
62
|
+
func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
|
|
63
|
+
switch t := typ.(type) {
|
|
64
|
+
case *types.Array:
|
|
65
|
+
c.tsw.WriteLiterally("[")
|
|
66
|
+
for i := 0; i < int(t.Len()); i++ {
|
|
67
|
+
if i > 0 {
|
|
68
|
+
c.tsw.WriteLiterally(", ")
|
|
69
|
+
}
|
|
70
|
+
c.WriteZeroValueForType(t.Elem())
|
|
71
|
+
}
|
|
72
|
+
c.tsw.WriteLiterally("]")
|
|
73
|
+
case *ast.Expr:
|
|
74
|
+
// For AST expressions, get the type and handle that instead
|
|
75
|
+
if expr := *t; expr != nil {
|
|
76
|
+
if typ := c.pkg.TypesInfo.TypeOf(expr); typ != nil {
|
|
77
|
+
c.WriteZeroValueForType(typ)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
c.tsw.WriteLiterally("null")
|
|
82
|
+
case *types.Basic:
|
|
83
|
+
switch t.Kind() {
|
|
84
|
+
case types.Bool:
|
|
85
|
+
c.tsw.WriteLiterally("false")
|
|
86
|
+
case types.String:
|
|
87
|
+
c.tsw.WriteLiterally(`""`)
|
|
88
|
+
default:
|
|
89
|
+
c.tsw.WriteLiterally("0")
|
|
90
|
+
}
|
|
91
|
+
case *types.Named:
|
|
92
|
+
// Handle named types, especially struct types
|
|
93
|
+
if _, isStruct := t.Underlying().(*types.Struct); isStruct {
|
|
94
|
+
// Initialize struct types with a new instance
|
|
95
|
+
c.tsw.WriteLiterallyf("new %s()", t.Obj().Name())
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
// For other named types, use the zero value of the underlying type
|
|
99
|
+
c.WriteZeroValueForType(t.Underlying())
|
|
100
|
+
case *types.Struct:
|
|
101
|
+
// For anonymous struct types, initialize with {}
|
|
102
|
+
c.tsw.WriteLiterally("{}")
|
|
103
|
+
default:
|
|
104
|
+
c.tsw.WriteLiterally("null")
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// WriteBasicType translates a Go basic type (primitives like int, string, bool)
|
|
109
|
+
// to its TypeScript equivalent.
|
|
110
|
+
// It handles untyped constants by mapping them to appropriate TypeScript types
|
|
111
|
+
// (boolean, number, string, null) and uses GoBuiltinToTypescript for typed primitives.
|
|
112
|
+
func (c *GoToTSCompiler) WriteBasicType(t *types.Basic) {
|
|
113
|
+
name := t.Name()
|
|
114
|
+
|
|
115
|
+
// Handle untyped constants by mapping them to appropriate TypeScript types
|
|
116
|
+
if t.Info()&types.IsUntyped != 0 {
|
|
117
|
+
switch t.Kind() {
|
|
118
|
+
case types.UntypedBool:
|
|
119
|
+
c.tsw.WriteLiterally("boolean")
|
|
120
|
+
return
|
|
121
|
+
case types.UntypedInt, types.UntypedFloat, types.UntypedComplex, types.UntypedRune:
|
|
122
|
+
c.tsw.WriteLiterally("number")
|
|
123
|
+
return
|
|
124
|
+
case types.UntypedString:
|
|
125
|
+
c.tsw.WriteLiterally("string")
|
|
126
|
+
return
|
|
127
|
+
case types.UntypedNil:
|
|
128
|
+
c.tsw.WriteLiterally("null")
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// For typed basic types, use the existing mapping
|
|
134
|
+
if tsType, ok := GoBuiltinToTypescript(name); ok {
|
|
135
|
+
c.tsw.WriteLiterally(tsType)
|
|
136
|
+
} else {
|
|
137
|
+
c.tsw.WriteLiterally(name)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// WriteNamedType translates a Go named type to its TypeScript equivalent.
|
|
142
|
+
// It specially handles the error interface as $.GoError, and uses the original
|
|
143
|
+
// type name for other named types.
|
|
144
|
+
func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
|
|
145
|
+
// Check if the named type is the error interface
|
|
146
|
+
if iface, ok := t.Underlying().(*types.Interface); ok && iface.String() == "interface{Error() string}" {
|
|
147
|
+
c.tsw.WriteLiterally("$.GoError")
|
|
148
|
+
} else {
|
|
149
|
+
// Use Obj().Name() for the original defined name
|
|
150
|
+
c.tsw.WriteLiterally(t.Obj().Name())
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// WritePointerType translates a Go pointer type (*T) to its TypeScript equivalent.
|
|
155
|
+
// It generates $.Box<T_ts> | null, where T_ts is the translated element type.
|
|
156
|
+
func (c *GoToTSCompiler) WritePointerType(t *types.Pointer) {
|
|
157
|
+
c.tsw.WriteLiterally("$.Box<")
|
|
158
|
+
c.WriteGoType(t.Elem())
|
|
159
|
+
c.tsw.WriteLiterally("> | null") // Pointers are always nullable
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// WriteSliceType translates a Go slice type ([]T) to its TypeScript equivalent.
|
|
163
|
+
// It generates $.Slice<T_ts>, where T_ts is the translated element type.
|
|
164
|
+
func (c *GoToTSCompiler) WriteSliceType(t *types.Slice) {
|
|
165
|
+
c.tsw.WriteLiterally("$.Slice<")
|
|
166
|
+
c.WriteGoType(t.Elem())
|
|
167
|
+
c.tsw.WriteLiterally(">")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// WriteArrayType translates a Go array type ([N]T) to its TypeScript equivalent.
|
|
171
|
+
// It generates T_ts[], where T_ts is the translated element type.
|
|
172
|
+
func (c *GoToTSCompiler) WriteArrayType(t *types.Array) {
|
|
173
|
+
c.WriteGoType(t.Elem())
|
|
174
|
+
c.tsw.WriteLiterally("[]") // Arrays cannot be nil
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// WriteMapType translates a Go map type (map[K]V) to its TypeScript equivalent.
|
|
178
|
+
// It generates Map<K_ts, V_ts>, where K_ts and V_ts are the translated key
|
|
179
|
+
// and element types respectively.
|
|
180
|
+
func (c *GoToTSCompiler) WriteMapType(t *types.Map) {
|
|
181
|
+
c.tsw.WriteLiterally("Map<")
|
|
182
|
+
c.WriteGoType(t.Key())
|
|
183
|
+
c.tsw.WriteLiterally(", ")
|
|
184
|
+
c.WriteGoType(t.Elem())
|
|
185
|
+
c.tsw.WriteLiterally(">")
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// WriteChannelType translates a Go channel type (chan T) to its TypeScript equivalent.
|
|
189
|
+
// It generates $.Channel<T_ts>, where T_ts is the translated element type.
|
|
190
|
+
func (c *GoToTSCompiler) WriteChannelType(t *types.Chan) {
|
|
191
|
+
c.tsw.WriteLiterally("$.Channel<")
|
|
192
|
+
c.WriteGoType(t.Elem())
|
|
193
|
+
c.tsw.WriteLiterally(">")
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// WriteFuncType translates a Go function type (`ast.FuncType`) into a TypeScript
|
|
197
|
+
// function signature.
|
|
198
|
+
// The signature is of the form `(param1: type1, param2: type2) => returnType`.
|
|
199
|
+
// - Parameters are written using `WriteFieldList`.
|
|
200
|
+
// - Return types:
|
|
201
|
+
// - If there are no results, the return type is `void`.
|
|
202
|
+
// - If there's a single, unnamed result, it's `resultType`.
|
|
203
|
+
// - If there are multiple or named results, it's a tuple type `[typeA, typeB]`.
|
|
204
|
+
// - If `isAsync` is true (indicating the function is known to perform async
|
|
205
|
+
// operations like channel interactions or contains `go` or `defer` with async calls),
|
|
206
|
+
// the return type is wrapped in `Promise<>` (e.g., `Promise<void>`, `Promise<number>`).
|
|
207
|
+
func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
|
|
208
|
+
c.tsw.WriteLiterally("(")
|
|
209
|
+
c.WriteFieldList(exp.Params, true) // true = arguments
|
|
210
|
+
c.tsw.WriteLiterally(")")
|
|
211
|
+
if exp.Results != nil && len(exp.Results.List) > 0 {
|
|
212
|
+
// Use colon for return type annotation
|
|
213
|
+
c.tsw.WriteLiterally(": ")
|
|
214
|
+
if isAsync {
|
|
215
|
+
c.tsw.WriteLiterally("Promise<")
|
|
216
|
+
}
|
|
217
|
+
if len(exp.Results.List) == 1 && len(exp.Results.List[0].Names) == 0 {
|
|
218
|
+
// Single unnamed return type
|
|
219
|
+
typ := c.pkg.TypesInfo.TypeOf(exp.Results.List[0].Type)
|
|
220
|
+
c.WriteGoType(typ)
|
|
221
|
+
} else {
|
|
222
|
+
// Multiple or named return types -> tuple
|
|
223
|
+
c.tsw.WriteLiterally("[")
|
|
224
|
+
for i, field := range exp.Results.List {
|
|
225
|
+
if i > 0 {
|
|
226
|
+
c.tsw.WriteLiterally(", ")
|
|
227
|
+
}
|
|
228
|
+
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
229
|
+
c.WriteGoType(typ)
|
|
230
|
+
}
|
|
231
|
+
c.tsw.WriteLiterally("]")
|
|
232
|
+
}
|
|
233
|
+
if isAsync {
|
|
234
|
+
c.tsw.WriteLiterally(">")
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
// No return value -> void
|
|
238
|
+
if isAsync {
|
|
239
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
240
|
+
} else {
|
|
241
|
+
c.tsw.WriteLiterally(": void")
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// WriteInterfaceType translates a Go interface type to its TypeScript equivalent.
|
|
247
|
+
// It specially handles the error interface as $.GoError, and delegates to
|
|
248
|
+
// writeInterfaceStructure for other interface types, prepending "null | ".
|
|
249
|
+
// If astNode is provided (e.g., from a type spec), comments for methods will be included.
|
|
250
|
+
func (c *GoToTSCompiler) WriteInterfaceType(t *types.Interface, astNode *ast.InterfaceType) {
|
|
251
|
+
// Handle the built-in error interface specifically
|
|
252
|
+
if t.String() == "interface{Error() string}" {
|
|
253
|
+
c.tsw.WriteLiterally("$.GoError")
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Prepend "null | " for all other interfaces.
|
|
258
|
+
// writeInterfaceStructure will handle the actual structure like "{...}" or "any".
|
|
259
|
+
c.tsw.WriteLiterally("null | ")
|
|
260
|
+
c.writeInterfaceStructure(t, astNode)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// WriteSignatureType translates a Go function signature to its TypeScript equivalent.
|
|
264
|
+
// It generates (param1: type1, param2: type2, ...): returnType for function types.
|
|
265
|
+
func (c *GoToTSCompiler) WriteSignatureType(t *types.Signature) {
|
|
266
|
+
c.tsw.WriteLiterally("(")
|
|
267
|
+
c.tsw.WriteLiterally("(")
|
|
268
|
+
params := t.Params()
|
|
269
|
+
for i := 0; i < params.Len(); i++ {
|
|
270
|
+
if i > 0 {
|
|
271
|
+
c.tsw.WriteLiterally(", ")
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
param := params.At(i)
|
|
275
|
+
paramSlice, paramIsSlice := param.Type().(*types.Slice)
|
|
276
|
+
|
|
277
|
+
paramVariadic := i == params.Len()-1 && t.Variadic()
|
|
278
|
+
if paramVariadic {
|
|
279
|
+
c.tsw.WriteLiterally("...")
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Use parameter name if available, otherwise use p0, p1, etc.
|
|
283
|
+
if param.Name() != "" {
|
|
284
|
+
c.tsw.WriteLiterally(param.Name())
|
|
285
|
+
} else {
|
|
286
|
+
c.tsw.WriteLiterallyf("p%d", i)
|
|
287
|
+
}
|
|
288
|
+
c.tsw.WriteLiterally(": ")
|
|
289
|
+
|
|
290
|
+
if paramVariadic && paramIsSlice {
|
|
291
|
+
c.WriteGoType(paramSlice.Elem())
|
|
292
|
+
c.tsw.WriteLiterally("[]")
|
|
293
|
+
} else {
|
|
294
|
+
c.WriteGoType(param.Type())
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
c.tsw.WriteLiterally(")")
|
|
298
|
+
|
|
299
|
+
// Handle return types
|
|
300
|
+
c.tsw.WriteLiterally(" => ")
|
|
301
|
+
results := t.Results()
|
|
302
|
+
if results.Len() == 0 {
|
|
303
|
+
c.tsw.WriteLiterally("void")
|
|
304
|
+
} else if results.Len() == 1 {
|
|
305
|
+
c.WriteGoType(results.At(0).Type())
|
|
306
|
+
} else {
|
|
307
|
+
// Multiple return values -> tuple
|
|
308
|
+
c.tsw.WriteLiterally("[")
|
|
309
|
+
for i := 0; i < results.Len(); i++ {
|
|
310
|
+
if i > 0 {
|
|
311
|
+
c.tsw.WriteLiterally(", ")
|
|
312
|
+
}
|
|
313
|
+
c.WriteGoType(results.At(i).Type())
|
|
314
|
+
}
|
|
315
|
+
c.tsw.WriteLiterally("]")
|
|
316
|
+
}
|
|
317
|
+
c.tsw.WriteLiterally(") | null")
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// writeInterfaceStructure translates a Go `types.Interface` into its TypeScript structural representation.
|
|
321
|
+
// If astNode is provided, it's used to fetch comments for methods.
|
|
322
|
+
// For example, an interface `interface { MethodA(x int) string; EmbeddedB }` might become
|
|
323
|
+
// `{ MethodA(_p0: number): string; } & B_ts`.
|
|
324
|
+
func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode *ast.InterfaceType) {
|
|
325
|
+
// Handle empty interface interface{}
|
|
326
|
+
if iface.NumExplicitMethods() == 0 && iface.NumEmbeddeds() == 0 {
|
|
327
|
+
c.tsw.WriteLiterally("any") // Matches current behavior for interface{}
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Keep track if we've written any part (methods or first embedded type)
|
|
332
|
+
// to correctly place " & " separators.
|
|
333
|
+
firstPartWritten := false
|
|
334
|
+
|
|
335
|
+
// Handle explicit methods
|
|
336
|
+
if iface.NumExplicitMethods() > 0 {
|
|
337
|
+
c.tsw.WriteLiterally("{") // Opening brace for the object type
|
|
338
|
+
c.tsw.Indent(1)
|
|
339
|
+
c.tsw.WriteLine("") // Newline after opening brace, before the first method
|
|
340
|
+
|
|
341
|
+
for i := 0; i < iface.NumExplicitMethods(); i++ {
|
|
342
|
+
method := iface.ExplicitMethod(i)
|
|
343
|
+
sig := method.Type().(*types.Signature)
|
|
344
|
+
|
|
345
|
+
// Find corresponding ast.Field for comments if astNode is available
|
|
346
|
+
var astField *ast.Field
|
|
347
|
+
if astNode != nil && astNode.Methods != nil {
|
|
348
|
+
for _, f := range astNode.Methods.List {
|
|
349
|
+
// Ensure the field is a named method (not an embedded interface)
|
|
350
|
+
if len(f.Names) > 0 && f.Names[0].Name == method.Name() {
|
|
351
|
+
astField = f
|
|
352
|
+
break
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Write comments if astField is found
|
|
358
|
+
if astField != nil {
|
|
359
|
+
if astField.Doc != nil {
|
|
360
|
+
c.WriteDoc(astField.Doc) // WriteDoc handles newlines
|
|
361
|
+
}
|
|
362
|
+
if astField.Comment != nil { // For trailing comments on the same line in Go AST
|
|
363
|
+
c.WriteDoc(astField.Comment)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
c.tsw.WriteLiterally(method.Name())
|
|
368
|
+
c.tsw.WriteLiterally("(") // Start params
|
|
369
|
+
params := sig.Params()
|
|
370
|
+
for j := 0; j < params.Len(); j++ {
|
|
371
|
+
if j > 0 {
|
|
372
|
+
c.tsw.WriteLiterally(", ")
|
|
373
|
+
}
|
|
374
|
+
paramVar := params.At(j)
|
|
375
|
+
paramName := paramVar.Name()
|
|
376
|
+
if paramName == "" || paramName == "_" {
|
|
377
|
+
paramName = fmt.Sprintf("_p%d", j)
|
|
378
|
+
}
|
|
379
|
+
c.tsw.WriteLiterally(paramName)
|
|
380
|
+
c.tsw.WriteLiterally(": ")
|
|
381
|
+
c.WriteGoType(paramVar.Type()) // Recursive call for param type
|
|
382
|
+
}
|
|
383
|
+
c.tsw.WriteLiterally(")") // End params
|
|
384
|
+
|
|
385
|
+
// Return type
|
|
386
|
+
c.tsw.WriteLiterally(": ")
|
|
387
|
+
results := sig.Results()
|
|
388
|
+
if results.Len() == 0 {
|
|
389
|
+
c.tsw.WriteLiterally("void")
|
|
390
|
+
} else if results.Len() == 1 {
|
|
391
|
+
c.WriteGoType(results.At(0).Type()) // Recursive call for result type
|
|
392
|
+
} else {
|
|
393
|
+
c.tsw.WriteLiterally("[")
|
|
394
|
+
for j := 0; j < results.Len(); j++ {
|
|
395
|
+
if j > 0 {
|
|
396
|
+
c.tsw.WriteLiterally(", ")
|
|
397
|
+
}
|
|
398
|
+
c.WriteGoType(results.At(j).Type()) // Recursive call for result type
|
|
399
|
+
}
|
|
400
|
+
c.tsw.WriteLiterally("]")
|
|
401
|
+
}
|
|
402
|
+
c.tsw.WriteLine("") // newline for each method
|
|
403
|
+
}
|
|
404
|
+
c.tsw.Indent(-1)
|
|
405
|
+
c.tsw.WriteLiterally("}") // Closing brace for the object type
|
|
406
|
+
firstPartWritten = true
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Handle embedded types
|
|
410
|
+
if iface.NumEmbeddeds() > 0 {
|
|
411
|
+
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
|
412
|
+
if firstPartWritten {
|
|
413
|
+
c.tsw.WriteLiterally(" & ")
|
|
414
|
+
} else {
|
|
415
|
+
// This is the first part being written (no explicit methods, only embedded)
|
|
416
|
+
firstPartWritten = true
|
|
417
|
+
}
|
|
418
|
+
embeddedType := iface.EmbeddedType(i)
|
|
419
|
+
// When WriteGoType encounters an interface, it will call WriteInterfaceType
|
|
420
|
+
// which will pass nil for astNode, so comments for deeply embedded interface literals
|
|
421
|
+
// might not be available unless they are named types.
|
|
422
|
+
c.WriteGoType(embeddedType)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// getTypeString is a utility function that converts a Go `types.Type` into its
|
|
428
|
+
// TypeScript type string representation. It achieves this by creating a temporary
|
|
429
|
+
// `GoToTSCompiler` and `TSCodeWriter` (writing to a `strings.Builder`) and then
|
|
430
|
+
// calling `WriteGoType` on the provided Go type. This allows reusing the main
|
|
431
|
+
// type translation logic to get a string representation of the TypeScript type.
|
|
432
|
+
func (c *GoToTSCompiler) getTypeString(goType types.Type) string {
|
|
433
|
+
var typeStr strings.Builder
|
|
434
|
+
writer := NewTSCodeWriter(&typeStr)
|
|
435
|
+
tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis)
|
|
436
|
+
tempCompiler.WriteGoType(goType)
|
|
437
|
+
return typeStr.String()
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// generateFlattenedInitTypeString generates a TypeScript type string for the
|
|
441
|
+
// initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
|
|
442
|
+
// The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
|
|
443
|
+
// including all direct and promoted fields of the `structType`.
|
|
444
|
+
// - It iterates through the direct fields of the `structType`. Exported fields
|
|
445
|
+
// are included with their TypeScript types (obtained via `WriteGoType`).
|
|
446
|
+
// - For anonymous (embedded) fields, it generates a type like `EmbeddedName?: ConstructorParameters<typeof EmbeddedName>[0]`,
|
|
447
|
+
// allowing initialization of the embedded struct's fields directly within the outer struct's initializer.
|
|
448
|
+
// - It then uses `types.NewMethodSet` and checks for `types.Var` objects that are fields
|
|
449
|
+
// to find promoted fields from embedded structs, adding them to the type string if not already present.
|
|
450
|
+
//
|
|
451
|
+
// The resulting string is sorted by field name for deterministic output and represents
|
|
452
|
+
// the shape of the object expected by the struct's TypeScript constructor.
|
|
453
|
+
func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named) string {
|
|
454
|
+
if structType == nil {
|
|
455
|
+
return "{}"
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Use a map to collect unique field names and their types
|
|
459
|
+
fieldMap := make(map[string]string)
|
|
460
|
+
embeddedTypeMap := make(map[string]string) // Stores TS type string for embedded struct initializers
|
|
461
|
+
|
|
462
|
+
underlying, ok := structType.Underlying().(*types.Struct)
|
|
463
|
+
if !ok {
|
|
464
|
+
return "{}"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// First add the direct fields and track embedded types
|
|
468
|
+
for i := 0; i < underlying.NumFields(); i++ {
|
|
469
|
+
field := underlying.Field(i)
|
|
470
|
+
fieldName := field.Name()
|
|
471
|
+
|
|
472
|
+
if !field.Exported() && field.Pkg() != c.pkg.Types {
|
|
473
|
+
continue
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if field.Anonymous() {
|
|
477
|
+
fieldType := field.Type()
|
|
478
|
+
isPtr := false
|
|
479
|
+
if ptr, ok := fieldType.(*types.Pointer); ok {
|
|
480
|
+
fieldType = ptr.Elem()
|
|
481
|
+
isPtr = true
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
485
|
+
embeddedName := named.Obj().Name()
|
|
486
|
+
// For embedded structs, the init type should allow providing fields of the embedded struct,
|
|
487
|
+
// or the embedded struct itself.
|
|
488
|
+
// We generate Partial<EmbeddedStructFields> | EmbeddedStructType
|
|
489
|
+
// This is complex. For now, let's use ConstructorParameters as before for simplicity,
|
|
490
|
+
// or allow the embedded struct type itself.
|
|
491
|
+
// The example `Person?: ConstructorParameters<typeof Person>[0]` is for the fields.
|
|
492
|
+
// Let's try to generate a type that allows either the embedded struct instance or its fields.
|
|
493
|
+
// This might be: `Partial<FlattenedInitType<EmbeddedName>> | EmbeddedName`
|
|
494
|
+
// For now, stick to the simpler `ConstructorParameters<typeof %s>[0]` which implies field-based init.
|
|
495
|
+
// Or, more simply, the type of the embedded struct itself if it's a pointer.
|
|
496
|
+
if isPtr {
|
|
497
|
+
embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type()) // MyEmbeddedType | null
|
|
498
|
+
} else {
|
|
499
|
+
// For value-type embedded structs, allow initializing with its fields.
|
|
500
|
+
// This requires getting the flattened init type for the embedded struct.
|
|
501
|
+
// This could lead to recursion if not handled carefully.
|
|
502
|
+
// A simpler approach for now: use the embedded struct's own type.
|
|
503
|
+
// embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type())
|
|
504
|
+
// Or, using ConstructorParameters to allow field-based initialization:
|
|
505
|
+
embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = fmt.Sprintf("Partial<ConstructorParameters<typeof %s>[0]>", embeddedName)
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
continue
|
|
509
|
+
}
|
|
510
|
+
fieldMap[fieldName] = c.getTypeString(field.Type())
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Promoted fields (handled by Go's embedding, init should use direct/embedded names)
|
|
514
|
+
// The current logic for `generateFlattenedInitTypeString` seems to focus on top-level
|
|
515
|
+
// settable properties in the constructor. Promoted fields are accessed via `this.promotedField`,
|
|
516
|
+
// not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
|
|
517
|
+
|
|
518
|
+
// Add embedded types to the field map (these are the names of the embedded structs themselves)
|
|
519
|
+
for embeddedName, embeddedTSType := range embeddedTypeMap {
|
|
520
|
+
fieldMap[embeddedName] = embeddedTSType
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
var fieldNames []string
|
|
524
|
+
for name := range fieldMap {
|
|
525
|
+
fieldNames = append(fieldNames, name)
|
|
526
|
+
}
|
|
527
|
+
sort.Strings(fieldNames)
|
|
528
|
+
|
|
529
|
+
var fieldDefs []string
|
|
530
|
+
for _, fieldName := range fieldNames {
|
|
531
|
+
fieldDefs = append(fieldDefs, fmt.Sprintf("%s?: %s", fieldName, fieldMap[fieldName]))
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return "{" + strings.Join(fieldDefs, ", ") + "}"
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// WriteStructType translates a Go struct type definition (`ast.StructType`)
|
|
538
|
+
// into a TypeScript anonymous object type (e.g., `{ Field1: Type1; Field2: Type2 }`).
|
|
539
|
+
// If the struct has no fields, it writes `{}`. Otherwise, it delegates to
|
|
540
|
+
// `WriteFieldList` to generate the list of field definitions.
|
|
541
|
+
// Note: This is for anonymous struct type literals. Named struct types are usually
|
|
542
|
+
// handled as classes via `WriteTypeSpec`.
|
|
543
|
+
func (c *GoToTSCompiler) WriteStructType(t *types.Struct) {
|
|
544
|
+
// Generate an interface with the struct's fields
|
|
545
|
+
c.tsw.WriteLiterally("{ ")
|
|
546
|
+
// Add field properties to the interface
|
|
547
|
+
for i := range t.NumFields() {
|
|
548
|
+
field := t.Field(i)
|
|
549
|
+
if i > 0 {
|
|
550
|
+
c.tsw.WriteLiterally("; ")
|
|
551
|
+
}
|
|
552
|
+
c.tsw.WriteLiterally(field.Name() + "?: ")
|
|
553
|
+
c.WriteGoType(field.Type())
|
|
554
|
+
}
|
|
555
|
+
c.tsw.WriteLiterally(" }")
|
|
556
|
+
}
|
|
@@ -170,12 +170,27 @@ export interface BaseTypeInfo {
|
|
|
170
170
|
kind: TypeKind;
|
|
171
171
|
zeroValue?: any;
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Represents an argument or a return value of a method.
|
|
175
|
+
*/
|
|
176
|
+
export interface MethodArg {
|
|
177
|
+
name?: string;
|
|
178
|
+
type: TypeInfo | string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Represents the signature of a method, including its name, arguments, and return types.
|
|
182
|
+
*/
|
|
183
|
+
export interface MethodSignature {
|
|
184
|
+
name: string;
|
|
185
|
+
args: MethodArg[];
|
|
186
|
+
returns: MethodArg[];
|
|
187
|
+
}
|
|
173
188
|
/**
|
|
174
189
|
* Type information for struct types
|
|
175
190
|
*/
|
|
176
191
|
export interface StructTypeInfo extends BaseTypeInfo {
|
|
177
192
|
kind: TypeKind.Struct;
|
|
178
|
-
methods:
|
|
193
|
+
methods: MethodSignature[];
|
|
179
194
|
ctor?: new (...args: any[]) => any;
|
|
180
195
|
fields: Record<string, TypeInfo | string>;
|
|
181
196
|
}
|
|
@@ -184,7 +199,7 @@ export interface StructTypeInfo extends BaseTypeInfo {
|
|
|
184
199
|
*/
|
|
185
200
|
export interface InterfaceTypeInfo extends BaseTypeInfo {
|
|
186
201
|
kind: TypeKind.Interface;
|
|
187
|
-
methods:
|
|
202
|
+
methods: MethodSignature[];
|
|
188
203
|
}
|
|
189
204
|
/**
|
|
190
205
|
* Type information for basic types (string, number, boolean)
|
|
@@ -229,6 +244,7 @@ export interface FunctionTypeInfo extends BaseTypeInfo {
|
|
|
229
244
|
kind: TypeKind.Function;
|
|
230
245
|
params?: (string | TypeInfo)[];
|
|
231
246
|
results?: (string | TypeInfo)[];
|
|
247
|
+
isVariadic?: boolean;
|
|
232
248
|
}
|
|
233
249
|
/**
|
|
234
250
|
* Type information for channel types
|
|
@@ -256,20 +272,21 @@ export declare function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeIn
|
|
|
256
272
|
*
|
|
257
273
|
* @param name The name of the type.
|
|
258
274
|
* @param zeroValue The zero value for the type.
|
|
259
|
-
* @param methods
|
|
275
|
+
* @param methods Array of method signatures for the struct.
|
|
260
276
|
* @param ctor Constructor for the struct.
|
|
277
|
+
* @param fields Record of field names and their types.
|
|
261
278
|
* @returns The struct type information object.
|
|
262
279
|
*/
|
|
263
|
-
export declare const registerStructType: (name: string, zeroValue: any, methods:
|
|
280
|
+
export declare const registerStructType: (name: string, zeroValue: any, methods: MethodSignature[], ctor: new (...args: any[]) => any, fields?: Record<string, TypeInfo | string>) => StructTypeInfo;
|
|
264
281
|
/**
|
|
265
282
|
* Registers an interface type with the runtime type system.
|
|
266
283
|
*
|
|
267
284
|
* @param name The name of the type.
|
|
268
285
|
* @param zeroValue The zero value for the type (usually null).
|
|
269
|
-
* @param methods
|
|
286
|
+
* @param methods Array of method signatures for the interface.
|
|
270
287
|
* @returns The interface type information object.
|
|
271
288
|
*/
|
|
272
|
-
export declare const registerInterfaceType: (name: string, zeroValue: any, methods:
|
|
289
|
+
export declare const registerInterfaceType: (name: string, zeroValue: any, methods: MethodSignature[]) => InterfaceTypeInfo;
|
|
273
290
|
/**
|
|
274
291
|
* Represents the result of a type assertion.
|
|
275
292
|
*/
|
|
@@ -277,6 +294,7 @@ export interface TypeAssertResult<T> {
|
|
|
277
294
|
value: T;
|
|
278
295
|
ok: boolean;
|
|
279
296
|
}
|
|
297
|
+
export declare function areTypeInfosIdentical(type1InfoOrName: string | TypeInfo, type2InfoOrName: string | TypeInfo): boolean;
|
|
280
298
|
export declare function typeAssert<T>(value: any, typeInfo: string | TypeInfo): TypeAssertResult<T>;
|
|
281
299
|
/**
|
|
282
300
|
* Represents the result of a channel receive operation with 'ok' value
|