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.
@@ -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
- func (c *GoToTSCompiler) getEmbeddedFieldKeyName(fieldType types.Type) string {
14
- trueType := fieldType
15
- if ptr, isPtr := trueType.(*types.Pointer); isPtr {
16
- trueType = ptr.Elem()
17
- }
18
-
19
- if named, isNamed := trueType.(*types.Named); isNamed {
20
- return named.Obj().Name()
21
- } else {
22
- // Fallback for unnamed embedded types, though less common for structs
23
- fieldKeyName := strings.Title(trueType.String()) // Simple heuristic
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 := 0; i < underlyingStruct.NumFields(); 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.WriteLine("this._fields = {")
225
- c.tsw.Indent(1)
97
+ c.tsw.WriteLiterally("this._fields = {")
226
98
 
227
99
  numFields := underlyingStruct.NumFields()
228
- for i := 0; i < numFields; i++ {
229
- field := underlyingStruct.Field(i)
230
- fieldType := field.Type()
231
- var fieldKeyName string
232
- if field.Anonymous() {
233
- fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
234
- } else {
235
- fieldKeyName = field.Name()
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
- c.writeBoxedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
114
+ c.writeBoxedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
239
115
 
240
- if i < numFields-1 {
241
- c.tsw.WriteLine(",")
242
- } else {
243
- c.tsw.WriteLine("")
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
- c.tsw.WriteLinef("public clone(): %s {", className)
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()", className)
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 := 0; i < numFields; 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
- if ident, ok := recvType.(*ast.Ident); ok && ident.Name == className {
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 := 0; i < goStructType.NumMethods(); 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 := 0; i < underlyingStruct.NumFields(); 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 := 0; k < embeddedMethodSet.Len(); 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.WriteLinef(" new Set([%s]),", c.collectMethodNames(className)) // collectMethodNames should ideally consider promoted methods too
466
- c.tsw.WriteLinef(" %s", className)
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
- // WriteInterfaceTypeSpec writes the TypeScript type for a Go interface type.
475
- func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.InterfaceType) error {
476
- c.tsw.WriteLiterally("type ")
477
- if err := c.WriteValueExpr(a.Name); err != nil {
478
- return err
479
- }
480
- c.tsw.WriteLiterally(" = ")
481
- // Get the types.Interface from the ast.InterfaceType.
482
- // For an interface definition like `type MyInterface interface { M() }`,
483
- // 't' is the *ast.InterfaceType representing `interface { M() }`.
484
- // TypesInfo.TypeOf(t) will give the *types.Interface.
485
- goType := c.pkg.TypesInfo.TypeOf(t)
486
- if goType == nil {
487
- return errors.Errorf("could not get type for interface AST node for %s", a.Name.Name)
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
- ifaceType, ok := goType.(*types.Interface)
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 errors.Errorf("expected *types.Interface, got %T for %s when processing interface literal", goType, a.Name.Name)
442
+ return "{}"
492
443
  }
493
- c.WriteInterfaceType(ifaceType, t) // Pass the *ast.InterfaceType for comment fetching
494
- c.tsw.WriteLine("")
495
444
 
496
- // Add code to register the interface with the runtime system
497
- interfaceName := a.Name.Name
498
- c.tsw.WriteLine("")
499
- c.tsw.WriteLinef("$.registerInterfaceType(")
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
- return nil
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
  }