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
|
@@ -4,159 +4,32 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/types"
|
|
7
|
+
"sort"
|
|
7
8
|
"strings"
|
|
8
|
-
|
|
9
|
-
// types provides type information for Go types.
|
|
10
|
-
"github.com/pkg/errors"
|
|
11
9
|
)
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if dotIndex := strings.LastIndex(fieldKeyName, "."); dotIndex != -1 {
|
|
25
|
-
fieldKeyName = fieldKeyName[dotIndex+1:]
|
|
26
|
-
}
|
|
27
|
-
return fieldKeyName
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func (c *GoToTSCompiler) writeGetterSetter(fieldName string, fieldType types.Type, doc, comment *ast.CommentGroup) {
|
|
32
|
-
fieldTypeStr := c.getTypeString(fieldType)
|
|
33
|
-
|
|
34
|
-
// Generate getter
|
|
35
|
-
if doc != nil {
|
|
36
|
-
c.WriteDoc(doc)
|
|
37
|
-
}
|
|
38
|
-
if comment != nil {
|
|
39
|
-
c.WriteDoc(comment)
|
|
40
|
-
}
|
|
41
|
-
c.tsw.WriteLinef("public get %s(): %s {", fieldName, fieldTypeStr)
|
|
42
|
-
c.tsw.Indent(1)
|
|
43
|
-
c.tsw.WriteLinef("return this._fields.%s.value", fieldName)
|
|
44
|
-
c.tsw.Indent(-1)
|
|
45
|
-
c.tsw.WriteLine("}")
|
|
46
|
-
|
|
47
|
-
// Generate setter (no comments)
|
|
48
|
-
c.tsw.WriteLinef("public set %s(value: %s) {", fieldName, fieldTypeStr)
|
|
49
|
-
c.tsw.Indent(1)
|
|
50
|
-
c.tsw.WriteLinef("this._fields.%s.value = value", fieldName)
|
|
51
|
-
c.tsw.Indent(-1)
|
|
52
|
-
c.tsw.WriteLine("}")
|
|
53
|
-
c.tsw.WriteLine("")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
func (c *GoToTSCompiler) writeBoxedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
57
|
-
c.tsw.WriteLiterally(fieldName)
|
|
58
|
-
c.tsw.WriteLiterally(": $.box(")
|
|
59
|
-
|
|
60
|
-
if isEmbedded {
|
|
61
|
-
if _, isPtr := fieldType.(*types.Pointer); isPtr {
|
|
62
|
-
c.tsw.WriteLiterallyf("init?.%s ?? null", fieldName)
|
|
63
|
-
} else {
|
|
64
|
-
typeForNew := fieldName
|
|
65
|
-
c.tsw.WriteLiterallyf("new %s(init?.%s)", typeForNew, fieldName)
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
isStructValueType := false
|
|
69
|
-
var structTypeNameForClone string
|
|
70
|
-
if named, ok := fieldType.(*types.Named); ok {
|
|
71
|
-
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
72
|
-
isStructValueType = true
|
|
73
|
-
structTypeNameForClone = named.Obj().Name()
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if isStructValueType {
|
|
78
|
-
c.tsw.WriteLiterallyf("init?.%s?.clone() ?? new %s()", fieldName, structTypeNameForClone)
|
|
79
|
-
} else {
|
|
80
|
-
c.tsw.WriteLiterallyf("init?.%s ?? ", fieldName)
|
|
81
|
-
c.WriteZeroValueForType(fieldType)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
c.tsw.WriteLiterally(")")
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
89
|
-
c.tsw.WriteLiterally(fieldName)
|
|
90
|
-
c.tsw.WriteLiterally(": $.box(")
|
|
91
|
-
|
|
92
|
-
if isEmbedded {
|
|
93
|
-
isPointerToStruct := false
|
|
94
|
-
trueType := fieldType
|
|
95
|
-
if ptr, isPtr := trueType.(*types.Pointer); isPtr {
|
|
96
|
-
trueType = ptr.Elem()
|
|
97
|
-
isPointerToStruct = true
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if named, isNamed := trueType.(*types.Named); isNamed {
|
|
101
|
-
_, isUnderlyingStruct := named.Underlying().(*types.Struct)
|
|
102
|
-
if isUnderlyingStruct && !isPointerToStruct { // Is a value struct
|
|
103
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value.clone()", fieldName)
|
|
104
|
-
} else { // Is a pointer to a struct, or not a struct
|
|
105
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
isValueTypeStruct := false
|
|
112
|
-
if named, ok := fieldType.(*types.Named); ok {
|
|
113
|
-
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
114
|
-
isValueTypeStruct = true
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if isValueTypeStruct {
|
|
119
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value?.clone() ?? null", fieldName)
|
|
120
|
-
} else {
|
|
121
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
c.tsw.WriteLiterally(")")
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// WriteTypeSpec writes the type specification to the output.
|
|
129
|
-
func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
130
|
-
if a.Doc != nil {
|
|
131
|
-
c.WriteDoc(a.Doc)
|
|
132
|
-
}
|
|
133
|
-
if a.Comment != nil {
|
|
134
|
-
c.WriteDoc(a.Comment)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
switch t := a.Type.(type) {
|
|
138
|
-
case *ast.StructType:
|
|
139
|
-
return c.WriteStructTypeSpec(a, t)
|
|
140
|
-
case *ast.InterfaceType:
|
|
141
|
-
return c.WriteInterfaceTypeSpec(a, t)
|
|
142
|
-
default:
|
|
143
|
-
// type alias
|
|
144
|
-
c.tsw.WriteLiterally("type ")
|
|
145
|
-
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
146
|
-
return err
|
|
147
|
-
}
|
|
148
|
-
c.tsw.WriteLiterally(" = ")
|
|
149
|
-
c.WriteTypeExpr(a.Type) // The aliased type
|
|
150
|
-
c.tsw.WriteLine(";")
|
|
151
|
-
}
|
|
152
|
-
return nil
|
|
153
|
-
}
|
|
154
|
-
|
|
11
|
+
// WriteStructTypeSpec generates the TypeScript class definition for a Go struct type.
|
|
12
|
+
// It handles the generation of:
|
|
13
|
+
// - The class declaration.
|
|
14
|
+
// - Getters and setters for all fields (both direct and embedded).
|
|
15
|
+
// - The internal `_fields` property, which stores field values in `$.Box` containers
|
|
16
|
+
// to maintain Go's value semantics.
|
|
17
|
+
// - A constructor that initializes the `_fields` and allows partial initialization.
|
|
18
|
+
// - A `clone` method for creating a deep copy of the struct instance.
|
|
19
|
+
// - Methods defined directly on the struct.
|
|
20
|
+
// - Wrapper methods for promoted fields and methods from embedded structs,
|
|
21
|
+
// ensuring correct access and behavior.
|
|
155
22
|
func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType) error {
|
|
156
23
|
c.tsw.WriteLiterally("class ")
|
|
157
24
|
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
158
25
|
return err
|
|
159
26
|
}
|
|
27
|
+
|
|
28
|
+
// Write type parameters if present (for generics)
|
|
29
|
+
if a.TypeParams != nil {
|
|
30
|
+
c.WriteTypeParameters(a.TypeParams)
|
|
31
|
+
}
|
|
32
|
+
|
|
160
33
|
c.tsw.WriteLiterally(" ")
|
|
161
34
|
c.tsw.WriteLine("{")
|
|
162
35
|
c.tsw.Indent(1)
|
|
@@ -188,7 +61,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
188
61
|
}
|
|
189
62
|
|
|
190
63
|
// Generate getters and setters for EMBEDDED struct fields themselves
|
|
191
|
-
for i :=
|
|
64
|
+
for i := range underlyingStruct.NumFields() {
|
|
192
65
|
field := underlyingStruct.Field(i)
|
|
193
66
|
if field.Anonymous() {
|
|
194
67
|
fieldKeyName := c.getEmbeddedFieldKeyName(field.Type())
|
|
@@ -221,43 +94,63 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
221
94
|
c.tsw.WriteLine("")
|
|
222
95
|
c.tsw.WriteLinef("constructor(init?: Partial<%s>) {", flattenedInitType)
|
|
223
96
|
c.tsw.Indent(1)
|
|
224
|
-
c.tsw.
|
|
225
|
-
c.tsw.Indent(1)
|
|
97
|
+
c.tsw.WriteLiterally("this._fields = {")
|
|
226
98
|
|
|
227
99
|
numFields := underlyingStruct.NumFields()
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
fieldKeyName
|
|
236
|
-
|
|
100
|
+
if numFields != 0 {
|
|
101
|
+
c.tsw.WriteLine("")
|
|
102
|
+
c.tsw.Indent(1)
|
|
103
|
+
|
|
104
|
+
for i := range numFields {
|
|
105
|
+
field := underlyingStruct.Field(i)
|
|
106
|
+
fieldType := field.Type()
|
|
107
|
+
var fieldKeyName string
|
|
108
|
+
if field.Anonymous() {
|
|
109
|
+
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
110
|
+
} else {
|
|
111
|
+
fieldKeyName = field.Name()
|
|
112
|
+
}
|
|
237
113
|
|
|
238
|
-
|
|
114
|
+
c.writeBoxedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
|
|
239
115
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
116
|
+
if i < numFields-1 {
|
|
117
|
+
c.tsw.WriteLine(",")
|
|
118
|
+
} else {
|
|
119
|
+
c.tsw.WriteLine("")
|
|
120
|
+
}
|
|
244
121
|
}
|
|
122
|
+
c.tsw.Indent(-1)
|
|
245
123
|
}
|
|
246
|
-
|
|
247
|
-
c.tsw.Indent(-1)
|
|
248
124
|
c.tsw.WriteLine("}")
|
|
125
|
+
|
|
249
126
|
c.tsw.Indent(-1)
|
|
250
127
|
c.tsw.WriteLine("}")
|
|
251
128
|
c.tsw.WriteLine("")
|
|
252
129
|
|
|
253
130
|
// Generate the clone method
|
|
254
|
-
|
|
131
|
+
cloneReturnType := className
|
|
132
|
+
if a.TypeParams != nil && len(a.TypeParams.List) > 0 {
|
|
133
|
+
cloneReturnType += "<"
|
|
134
|
+
first := true
|
|
135
|
+
for _, field := range a.TypeParams.List {
|
|
136
|
+
for _, name := range field.Names {
|
|
137
|
+
if !first {
|
|
138
|
+
cloneReturnType += ", "
|
|
139
|
+
}
|
|
140
|
+
first = false
|
|
141
|
+
cloneReturnType += name.Name
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
cloneReturnType += ">"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
c.tsw.WriteLinef("public clone(): %s {", cloneReturnType)
|
|
255
148
|
c.tsw.Indent(1)
|
|
256
|
-
c.tsw.WriteLinef("const cloned = new %s()",
|
|
149
|
+
c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType)
|
|
257
150
|
c.tsw.WriteLine("cloned._fields = {")
|
|
258
151
|
c.tsw.Indent(1)
|
|
259
152
|
|
|
260
|
-
for i :=
|
|
153
|
+
for i := range numFields {
|
|
261
154
|
field := underlyingStruct.Field(i)
|
|
262
155
|
fieldType := field.Type()
|
|
263
156
|
var fieldKeyName string
|
|
@@ -294,7 +187,18 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
294
187
|
if starExpr, ok := recvType.(*ast.StarExpr); ok {
|
|
295
188
|
recvType = starExpr.X
|
|
296
189
|
}
|
|
297
|
-
|
|
190
|
+
|
|
191
|
+
// Check for both simple identifiers (Pair) and generic types (Pair[T])
|
|
192
|
+
var recvTypeName string
|
|
193
|
+
if ident, ok := recvType.(*ast.Ident); ok {
|
|
194
|
+
recvTypeName = ident.Name
|
|
195
|
+
} else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
|
|
196
|
+
if ident, ok := indexExpr.X.(*ast.Ident); ok {
|
|
197
|
+
recvTypeName = ident.Name
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if recvTypeName == className {
|
|
298
202
|
c.tsw.WriteLine("")
|
|
299
203
|
if err := c.WriteFuncDeclAsMethod(funcDecl); err != nil {
|
|
300
204
|
return err
|
|
@@ -307,7 +211,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
307
211
|
seenPromotedFields := make(map[string]bool)
|
|
308
212
|
directMethods := make(map[string]bool)
|
|
309
213
|
// Populate directMethods (methods defined directly on this struct type)
|
|
310
|
-
for i :=
|
|
214
|
+
for i := range goStructType.NumMethods() {
|
|
311
215
|
method := goStructType.Method(i)
|
|
312
216
|
sig := method.Type().(*types.Signature)
|
|
313
217
|
if sig.Recv() != nil {
|
|
@@ -322,7 +226,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
322
226
|
}
|
|
323
227
|
}
|
|
324
228
|
|
|
325
|
-
for i :=
|
|
229
|
+
for i := range underlyingStruct.NumFields() {
|
|
326
230
|
field := underlyingStruct.Field(i)
|
|
327
231
|
if !field.Anonymous() {
|
|
328
232
|
continue
|
|
@@ -383,7 +287,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
383
287
|
|
|
384
288
|
// Promoted methods
|
|
385
289
|
embeddedMethodSet := types.NewMethodSet(embeddedFieldType) // Use original field type for method set
|
|
386
|
-
for k :=
|
|
290
|
+
for k := range embeddedMethodSet.Len() {
|
|
387
291
|
methodSelection := embeddedMethodSet.At(k)
|
|
388
292
|
method := methodSelection.Obj().(*types.Func)
|
|
389
293
|
methodName := method.Name()
|
|
@@ -422,21 +326,21 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
422
326
|
}
|
|
423
327
|
c.tsw.WriteLiterally(paramName)
|
|
424
328
|
c.tsw.WriteLiterally(": ")
|
|
425
|
-
c.WriteGoType(param.Type())
|
|
329
|
+
c.WriteGoType(param.Type(), GoTypeContextGeneral)
|
|
426
330
|
}
|
|
427
331
|
c.tsw.WriteLiterally(")")
|
|
428
332
|
results := sig.Results()
|
|
429
333
|
if results.Len() > 0 {
|
|
430
334
|
c.tsw.WriteLiterally(": ")
|
|
431
335
|
if results.Len() == 1 {
|
|
432
|
-
c.WriteGoType(results.At(0).Type())
|
|
336
|
+
c.WriteGoType(results.At(0).Type(), GoTypeContextFunctionReturn)
|
|
433
337
|
} else {
|
|
434
338
|
c.tsw.WriteLiterally("[")
|
|
435
339
|
for j := 0; j < results.Len(); j++ {
|
|
436
340
|
if j > 0 {
|
|
437
341
|
c.tsw.WriteLiterally(", ")
|
|
438
342
|
}
|
|
439
|
-
c.WriteGoType(results.At(j).Type())
|
|
343
|
+
c.WriteGoType(results.At(j).Type(), GoTypeContextFunctionReturn)
|
|
440
344
|
}
|
|
441
345
|
c.tsw.WriteLiterally("]")
|
|
442
346
|
}
|
|
@@ -456,14 +360,54 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
456
360
|
}
|
|
457
361
|
}
|
|
458
362
|
|
|
459
|
-
// Add code to register the type with the runtime system
|
|
363
|
+
// Add code to register the type with the runtime type system
|
|
460
364
|
c.tsw.WriteLine("")
|
|
461
365
|
c.tsw.WriteLinef("// Register this type with the runtime type system")
|
|
462
366
|
c.tsw.WriteLinef("static __typeInfo = $.registerStructType(")
|
|
463
367
|
c.tsw.WriteLinef(" '%s',", className)
|
|
464
368
|
c.tsw.WriteLinef(" new %s(),", className)
|
|
465
|
-
c.tsw.
|
|
466
|
-
|
|
369
|
+
c.tsw.WriteLiterally(" [")
|
|
370
|
+
// Collect methods for the struct type
|
|
371
|
+
var structMethods []*types.Func
|
|
372
|
+
for i := range goStructType.NumMethods() {
|
|
373
|
+
method := goStructType.Method(i)
|
|
374
|
+
// Ensure it's a method directly on this type (not promoted here, promotion handled separately)
|
|
375
|
+
// Check if receiver is *T or T where T is goStructType
|
|
376
|
+
sig := method.Type().(*types.Signature)
|
|
377
|
+
recv := sig.Recv().Type()
|
|
378
|
+
if ptr, ok := recv.(*types.Pointer); ok {
|
|
379
|
+
recv = ptr.Elem()
|
|
380
|
+
}
|
|
381
|
+
if namedRecv, ok := recv.(*types.Named); ok && namedRecv.Obj() == goStructType.Obj() {
|
|
382
|
+
structMethods = append(structMethods, method)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
c.writeMethodSignatures(structMethods)
|
|
386
|
+
c.tsw.WriteLiterally("],")
|
|
387
|
+
c.tsw.WriteLine("")
|
|
388
|
+
|
|
389
|
+
c.tsw.WriteLinef(" %s,", className)
|
|
390
|
+
// Add field type information for type assertions
|
|
391
|
+
c.tsw.WriteLiterally(" {")
|
|
392
|
+
firstField := true
|
|
393
|
+
for i := 0; i < underlyingStruct.NumFields(); i++ {
|
|
394
|
+
field := underlyingStruct.Field(i)
|
|
395
|
+
var fieldKeyName string
|
|
396
|
+
if field.Anonymous() {
|
|
397
|
+
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
398
|
+
} else {
|
|
399
|
+
fieldKeyName = field.Name()
|
|
400
|
+
}
|
|
401
|
+
// fieldTsType := c.getTypeString(field.Type())
|
|
402
|
+
if !firstField {
|
|
403
|
+
c.tsw.WriteLiterally(", ")
|
|
404
|
+
}
|
|
405
|
+
firstField = false
|
|
406
|
+
c.tsw.WriteLiterallyf("%q: ", fieldKeyName)
|
|
407
|
+
c.writeTypeInfoObject(field.Type()) // Use writeTypeInfoObject for field types
|
|
408
|
+
}
|
|
409
|
+
c.tsw.WriteLiterally("}")
|
|
410
|
+
c.tsw.WriteLine("")
|
|
467
411
|
c.tsw.WriteLinef(");")
|
|
468
412
|
|
|
469
413
|
c.tsw.Indent(-1)
|
|
@@ -471,36 +415,99 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
471
415
|
return nil
|
|
472
416
|
}
|
|
473
417
|
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
418
|
+
// generateFlattenedInitTypeString generates a TypeScript type string for the
|
|
419
|
+
// initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
|
|
420
|
+
// The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
|
|
421
|
+
// including all direct and promoted fields of the `structType`.
|
|
422
|
+
// - It iterates through the direct fields of the `structType`. Exported fields
|
|
423
|
+
// are included with their TypeScript types (obtained via `WriteGoType`).
|
|
424
|
+
// - For anonymous (embedded) fields, it generates a type like `EmbeddedName?: ConstructorParameters<typeof EmbeddedName>[0]`,
|
|
425
|
+
// allowing initialization of the embedded struct's fields directly within the outer struct's initializer.
|
|
426
|
+
// - It then uses `types.NewMethodSet` and checks for `types.Var` objects that are fields
|
|
427
|
+
// to find promoted fields from embedded structs, adding them to the type string if not already present.
|
|
428
|
+
//
|
|
429
|
+
// The resulting string is sorted by field name for deterministic output and represents
|
|
430
|
+
// the shape of the object expected by the struct's TypeScript constructor.
|
|
431
|
+
func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named) string {
|
|
432
|
+
if structType == nil {
|
|
433
|
+
return "{}"
|
|
488
434
|
}
|
|
489
|
-
|
|
435
|
+
|
|
436
|
+
// Use a map to collect unique field names and their types
|
|
437
|
+
fieldMap := make(map[string]string)
|
|
438
|
+
embeddedTypeMap := make(map[string]string) // Stores TS type string for embedded struct initializers
|
|
439
|
+
|
|
440
|
+
underlying, ok := structType.Underlying().(*types.Struct)
|
|
490
441
|
if !ok {
|
|
491
|
-
return
|
|
442
|
+
return "{}"
|
|
492
443
|
}
|
|
493
|
-
c.WriteInterfaceType(ifaceType, t) // Pass the *ast.InterfaceType for comment fetching
|
|
494
|
-
c.tsw.WriteLine("")
|
|
495
444
|
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
c.tsw.WriteLinef(" '%s',", interfaceName)
|
|
501
|
-
c.tsw.WriteLinef(" null, // Zero value for interface is null")
|
|
502
|
-
c.tsw.WriteLinef(" new Set([%s]),", c.collectInterfaceMethods(t))
|
|
503
|
-
c.tsw.WriteLinef(");")
|
|
445
|
+
// First add the direct fields and track embedded types
|
|
446
|
+
for i := 0; i < underlying.NumFields(); i++ {
|
|
447
|
+
field := underlying.Field(i)
|
|
448
|
+
fieldName := field.Name()
|
|
504
449
|
|
|
505
|
-
|
|
450
|
+
if !field.Exported() && field.Pkg() != c.pkg.Types {
|
|
451
|
+
continue
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if field.Anonymous() {
|
|
455
|
+
fieldType := field.Type()
|
|
456
|
+
isPtr := false
|
|
457
|
+
if ptr, ok := fieldType.(*types.Pointer); ok {
|
|
458
|
+
fieldType = ptr.Elem()
|
|
459
|
+
isPtr = true
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
463
|
+
embeddedName := named.Obj().Name()
|
|
464
|
+
// For embedded structs, the init type should allow providing fields of the embedded struct,
|
|
465
|
+
// or the embedded struct itself.
|
|
466
|
+
// We generate Partial<EmbeddedStructFields> | EmbeddedStructType
|
|
467
|
+
// This is complex. For now, let's use ConstructorParameters as before for simplicity,
|
|
468
|
+
// or allow the embedded struct type itself.
|
|
469
|
+
// The example `Person?: ConstructorParameters<typeof Person>[0]` is for the fields.
|
|
470
|
+
// Let's try to generate a type that allows either the embedded struct instance or its fields.
|
|
471
|
+
// This might be: `Partial<FlattenedInitType<EmbeddedName>> | EmbeddedName`
|
|
472
|
+
// For now, stick to the simpler `ConstructorParameters<typeof %s>[0]` which implies field-based init.
|
|
473
|
+
// Or, more simply, the type of the embedded struct itself if it's a pointer.
|
|
474
|
+
if isPtr {
|
|
475
|
+
embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type()) // MyEmbeddedType | null
|
|
476
|
+
} else {
|
|
477
|
+
// For value-type embedded structs, allow initializing with its fields.
|
|
478
|
+
// This requires getting the flattened init type for the embedded struct.
|
|
479
|
+
// This could lead to recursion if not handled carefully.
|
|
480
|
+
// A simpler approach for now: use the embedded struct's own type.
|
|
481
|
+
// embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type())
|
|
482
|
+
// Or, using ConstructorParameters to allow field-based initialization:
|
|
483
|
+
embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = fmt.Sprintf("Partial<ConstructorParameters<typeof %s>[0]>", embeddedName)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
continue
|
|
487
|
+
}
|
|
488
|
+
fieldMap[fieldName] = c.getTypeString(field.Type())
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Promoted fields (handled by Go's embedding, init should use direct/embedded names)
|
|
492
|
+
// The current logic for `generateFlattenedInitTypeString` seems to focus on top-level
|
|
493
|
+
// settable properties in the constructor. Promoted fields are accessed via `this.promotedField`,
|
|
494
|
+
// not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
|
|
495
|
+
|
|
496
|
+
// Add embedded types to the field map (these are the names of the embedded structs themselves)
|
|
497
|
+
for embeddedName, embeddedTSType := range embeddedTypeMap {
|
|
498
|
+
fieldMap[embeddedName] = embeddedTSType
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
var fieldNames []string
|
|
502
|
+
for name := range fieldMap {
|
|
503
|
+
fieldNames = append(fieldNames, name)
|
|
504
|
+
}
|
|
505
|
+
sort.Strings(fieldNames)
|
|
506
|
+
|
|
507
|
+
var fieldDefs []string
|
|
508
|
+
for _, fieldName := range fieldNames {
|
|
509
|
+
fieldDefs = append(fieldDefs, fmt.Sprintf("%s?: %s", fieldName, fieldMap[fieldName]))
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return "{" + strings.Join(fieldDefs, ", ") + "}"
|
|
506
513
|
}
|