goscript 0.0.20 → 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 +390 -119
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +203 -5879
- 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/output.go +1 -1
- 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
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
"go/types"
|
|
8
|
+
"strings"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// writeTypeAssert handles the Go type assertion with comma-ok idiom in an
|
|
12
|
+
// assignment context: `value, ok := interfaceExpr.(AssertedType)` (or with `=`).
|
|
13
|
+
// It translates this to a TypeScript destructuring assignment (or declaration if `tok`
|
|
14
|
+
// is `token.DEFINE` for `:=`) using the `$.typeAssert` runtime helper.
|
|
15
|
+
//
|
|
16
|
+
// The generated TypeScript is:
|
|
17
|
+
// `[let] { value: valueName, ok: okName } = $.typeAssert<AssertedType_ts>(interfaceExpr_ts, 'AssertedTypeName');`
|
|
18
|
+
//
|
|
19
|
+
// - `AssertedType_ts` is the TypeScript translation of `AssertedType`.
|
|
20
|
+
// - `interfaceExpr_ts` is the TypeScript translation of `interfaceExpr`.
|
|
21
|
+
// - `'AssertedTypeName'` is a string representation of the asserted type name,
|
|
22
|
+
// obtained via `getTypeNameString`, used for runtime error messages.
|
|
23
|
+
// - `valueName` and `okName` are the Go variable names from the LHS.
|
|
24
|
+
// - Blank identifiers (`_`) on the LHS are handled by omitting the corresponding
|
|
25
|
+
// property in the destructuring pattern (e.g., `{ ok: okName } = ...` if `value` is blank).
|
|
26
|
+
// - If `tok` is not `token.DEFINE` (i.e., for regular assignment `=`), the entire
|
|
27
|
+
// destructuring assignment is wrapped in parentheses `(...)` to make it a valid
|
|
28
|
+
// expression if needed, though typically assignments are statements.
|
|
29
|
+
//
|
|
30
|
+
// The statement is terminated with a newline.
|
|
31
|
+
func (c *GoToTSCompiler) writeTypeAssert(lhs []ast.Expr, typeAssertExpr *ast.TypeAssertExpr, tok token.Token) error {
|
|
32
|
+
interfaceExpr := typeAssertExpr.X
|
|
33
|
+
assertedType := typeAssertExpr.Type
|
|
34
|
+
|
|
35
|
+
// Unwrap parenthesized expressions to handle cases like r.((<-chan T))
|
|
36
|
+
for {
|
|
37
|
+
if parenExpr, ok := assertedType.(*ast.ParenExpr); ok {
|
|
38
|
+
assertedType = parenExpr.X
|
|
39
|
+
} else {
|
|
40
|
+
break
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Ensure LHS has exactly two expressions (value and ok)
|
|
45
|
+
if len(lhs) != 2 {
|
|
46
|
+
return fmt.Errorf("type assertion assignment requires exactly 2 variables on LHS, got %d", len(lhs))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get variable names, handling blank identifiers
|
|
50
|
+
valueIsBlank := false
|
|
51
|
+
okIsBlank := false
|
|
52
|
+
var valueName string
|
|
53
|
+
var okName string
|
|
54
|
+
var valueIdent *ast.Ident
|
|
55
|
+
var okIdent *ast.Ident
|
|
56
|
+
|
|
57
|
+
if valId, ok := lhs[0].(*ast.Ident); ok {
|
|
58
|
+
valueIdent = valId
|
|
59
|
+
if valId.Name == "_" {
|
|
60
|
+
valueIsBlank = true
|
|
61
|
+
} else {
|
|
62
|
+
valueName = valId.Name
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
return fmt.Errorf("unhandled LHS expression type for value in type assertion: %T", lhs[0])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if okId, ok := lhs[1].(*ast.Ident); ok {
|
|
69
|
+
okIdent = okId
|
|
70
|
+
if okId.Name == "_" {
|
|
71
|
+
okIsBlank = true
|
|
72
|
+
} else {
|
|
73
|
+
okName = okId.Name
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
return fmt.Errorf("unhandled LHS expression type for ok in type assertion: %T", lhs[1])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// For token.DEFINE (:=), we need to check if any of the variables are already declared
|
|
80
|
+
// In Go, := can be used for redeclaration if at least one variable is new
|
|
81
|
+
writeEndParen := false
|
|
82
|
+
if tok == token.DEFINE {
|
|
83
|
+
// Identify which variables are new vs existing
|
|
84
|
+
valueIsNew := true
|
|
85
|
+
okIsNew := true
|
|
86
|
+
anyNewVars := false
|
|
87
|
+
allNewVars := true
|
|
88
|
+
|
|
89
|
+
// Check if variables are already in scope
|
|
90
|
+
if !valueIsBlank {
|
|
91
|
+
if obj := c.pkg.TypesInfo.Uses[valueIdent]; obj != nil {
|
|
92
|
+
// If it's in Uses, it's referenced elsewhere, so it exists
|
|
93
|
+
valueIsNew = false
|
|
94
|
+
allNewVars = false
|
|
95
|
+
}
|
|
96
|
+
if valueIsNew {
|
|
97
|
+
anyNewVars = true
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if !okIsBlank {
|
|
102
|
+
if obj := c.pkg.TypesInfo.Uses[okIdent]; obj != nil {
|
|
103
|
+
// If it's in Uses, it's referenced elsewhere, so it exists
|
|
104
|
+
okIsNew = false
|
|
105
|
+
allNewVars = false
|
|
106
|
+
}
|
|
107
|
+
if okIsNew {
|
|
108
|
+
anyNewVars = true
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if allNewVars && anyNewVars {
|
|
113
|
+
c.tsw.WriteLiterally("let ")
|
|
114
|
+
} else if anyNewVars {
|
|
115
|
+
// If only some variables are new, declare them separately
|
|
116
|
+
if !valueIsBlank && valueIsNew {
|
|
117
|
+
c.tsw.WriteLiterally("let ")
|
|
118
|
+
c.tsw.WriteLiterally(valueName)
|
|
119
|
+
// Add type annotation if possible
|
|
120
|
+
if tv, ok := c.pkg.TypesInfo.Types[assertedType]; ok {
|
|
121
|
+
c.tsw.WriteLiterally(": ")
|
|
122
|
+
c.WriteGoType(tv.Type)
|
|
123
|
+
}
|
|
124
|
+
c.tsw.WriteLine("")
|
|
125
|
+
}
|
|
126
|
+
if !okIsBlank && okIsNew {
|
|
127
|
+
c.tsw.WriteLiterally("let ")
|
|
128
|
+
c.tsw.WriteLiterally(okName)
|
|
129
|
+
c.tsw.WriteLiterally(": boolean") // ok is always boolean
|
|
130
|
+
c.tsw.WriteLine("")
|
|
131
|
+
}
|
|
132
|
+
// Use parenthesized destructuring assignment for existing variables
|
|
133
|
+
c.tsw.WriteLiterally(";(")
|
|
134
|
+
writeEndParen = true
|
|
135
|
+
} else {
|
|
136
|
+
// All variables exist, use parenthesized destructuring assignment
|
|
137
|
+
c.tsw.WriteLiterally(";(")
|
|
138
|
+
writeEndParen = true
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
c.tsw.WriteLiterally("(")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
c.tsw.WriteLiterally("{ ")
|
|
145
|
+
// Dynamically build the destructuring pattern
|
|
146
|
+
parts := []string{}
|
|
147
|
+
if !valueIsBlank {
|
|
148
|
+
parts = append(parts, fmt.Sprintf("value: %s", valueName))
|
|
149
|
+
}
|
|
150
|
+
if !okIsBlank {
|
|
151
|
+
parts = append(parts, fmt.Sprintf("ok: %s", okName))
|
|
152
|
+
}
|
|
153
|
+
c.tsw.WriteLiterally(strings.Join(parts, ", "))
|
|
154
|
+
c.tsw.WriteLiterally(" } = $.typeAssert<")
|
|
155
|
+
|
|
156
|
+
// Write the asserted type for the generic
|
|
157
|
+
c.WriteTypeExpr(assertedType)
|
|
158
|
+
c.tsw.WriteLiterally(">(")
|
|
159
|
+
|
|
160
|
+
// Write the interface expression
|
|
161
|
+
if err := c.WriteValueExpr(interfaceExpr); err != nil {
|
|
162
|
+
return fmt.Errorf("failed to write interface expression in type assertion call: %w", err)
|
|
163
|
+
}
|
|
164
|
+
c.tsw.WriteLiterally(", ")
|
|
165
|
+
|
|
166
|
+
// Use structured type information for all types
|
|
167
|
+
switch typeExpr := assertedType.(type) {
|
|
168
|
+
case *ast.MapType:
|
|
169
|
+
c.tsw.WriteLiterally("{")
|
|
170
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Map, ")
|
|
171
|
+
|
|
172
|
+
c.tsw.WriteLiterally("keyType: ")
|
|
173
|
+
if ident, ok := typeExpr.Key.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
|
|
174
|
+
if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
|
|
175
|
+
c.tsw.WriteLiterallyf("'%s'", tsType)
|
|
176
|
+
} else {
|
|
177
|
+
c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
c.writeTypeDescription(typeExpr.Key)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
c.tsw.WriteLiterally(", ")
|
|
184
|
+
|
|
185
|
+
// Add element type
|
|
186
|
+
c.tsw.WriteLiterally("elemType: ")
|
|
187
|
+
if ident, ok := typeExpr.Value.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
|
|
188
|
+
if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
|
|
189
|
+
c.tsw.WriteLiterallyf("'%s'", tsType)
|
|
190
|
+
} else {
|
|
191
|
+
c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
c.writeTypeDescription(typeExpr.Value)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
c.tsw.WriteLiterally("}")
|
|
198
|
+
case *ast.ArrayType:
|
|
199
|
+
// Determine if it's a slice or array
|
|
200
|
+
typeKind := "$.TypeKind.Slice"
|
|
201
|
+
if typeExpr.Len != nil {
|
|
202
|
+
typeKind = "$.TypeKind.Array"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Create a type descriptor object
|
|
206
|
+
c.tsw.WriteLiterally("{")
|
|
207
|
+
c.tsw.WriteLiterallyf("kind: %s, ", typeKind)
|
|
208
|
+
|
|
209
|
+
// Add element type
|
|
210
|
+
c.tsw.WriteLiterally("elemType: ")
|
|
211
|
+
if ident, ok := typeExpr.Elt.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
|
|
212
|
+
if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
|
|
213
|
+
c.tsw.WriteLiterallyf("'%s'", tsType)
|
|
214
|
+
} else {
|
|
215
|
+
c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
c.writeTypeDescription(typeExpr.Elt)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
c.tsw.WriteLiterally("}")
|
|
222
|
+
case *ast.StructType:
|
|
223
|
+
// For struct types, create a type descriptor object
|
|
224
|
+
c.tsw.WriteLiterally("{")
|
|
225
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Struct")
|
|
226
|
+
|
|
227
|
+
// Get the type name if available
|
|
228
|
+
typeName := c.getTypeNameString(assertedType)
|
|
229
|
+
if typeName != "unknown" {
|
|
230
|
+
c.tsw.WriteLiterallyf(", name: '%s'", typeName)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if typeExpr.Fields != nil && typeExpr.Fields.List != nil {
|
|
234
|
+
// Add fields property to provide type information
|
|
235
|
+
c.tsw.WriteLiterally(", fields: {")
|
|
236
|
+
|
|
237
|
+
hasFields := false
|
|
238
|
+
for _, field := range typeExpr.Fields.List {
|
|
239
|
+
if len(field.Names) > 0 {
|
|
240
|
+
for _, name := range field.Names {
|
|
241
|
+
if hasFields {
|
|
242
|
+
c.tsw.WriteLiterally(", ")
|
|
243
|
+
}
|
|
244
|
+
c.tsw.WriteLiterally(fmt.Sprintf("'%s': ", name.Name))
|
|
245
|
+
c.writeTypeDescription(field.Type)
|
|
246
|
+
hasFields = true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
c.tsw.WriteLiterally("}")
|
|
252
|
+
} else {
|
|
253
|
+
c.tsw.WriteLiterally(", fields: {}")
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Add empty methods set to satisfy StructTypeInfo interface
|
|
257
|
+
c.tsw.WriteLiterally(", methods: []")
|
|
258
|
+
|
|
259
|
+
c.tsw.WriteLiterally("}")
|
|
260
|
+
case *ast.InterfaceType:
|
|
261
|
+
c.tsw.WriteLiterally("{")
|
|
262
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Interface")
|
|
263
|
+
|
|
264
|
+
// Get the type name if available
|
|
265
|
+
typeName := c.getTypeNameString(assertedType)
|
|
266
|
+
if typeName != "unknown" {
|
|
267
|
+
c.tsw.WriteLiterallyf(", name: '%s'", typeName)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Add methods if available
|
|
271
|
+
c.tsw.WriteLiterally(", methods: []")
|
|
272
|
+
|
|
273
|
+
c.tsw.WriteLiterally("}")
|
|
274
|
+
case *ast.StarExpr:
|
|
275
|
+
c.tsw.WriteLiterally("{")
|
|
276
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Pointer")
|
|
277
|
+
|
|
278
|
+
// Add element type if it's a struct type or named type
|
|
279
|
+
if structType, ok := typeExpr.X.(*ast.StructType); ok {
|
|
280
|
+
c.tsw.WriteLiterally(", elemType: ")
|
|
281
|
+
c.writeTypeDescription(structType)
|
|
282
|
+
} else if ident, ok := typeExpr.X.(*ast.Ident); ok {
|
|
283
|
+
c.tsw.WriteLiterallyf(", elemType: '%s'", ident.Name)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
c.tsw.WriteLiterally("}")
|
|
287
|
+
case *ast.ChanType:
|
|
288
|
+
c.tsw.WriteLiterally("{")
|
|
289
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Channel")
|
|
290
|
+
|
|
291
|
+
// Add element type
|
|
292
|
+
c.tsw.WriteLiterally(", elemType: ")
|
|
293
|
+
if ident, ok := typeExpr.Value.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
|
|
294
|
+
if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
|
|
295
|
+
c.tsw.WriteLiterallyf("'%s'", tsType)
|
|
296
|
+
} else {
|
|
297
|
+
c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
c.writeTypeDescription(typeExpr.Value)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
c.tsw.WriteLiterally(", direction: ")
|
|
304
|
+
switch typeExpr.Dir {
|
|
305
|
+
case ast.SEND:
|
|
306
|
+
c.tsw.WriteLiterally("'send'")
|
|
307
|
+
case ast.RECV:
|
|
308
|
+
c.tsw.WriteLiterally("'receive'")
|
|
309
|
+
case ast.SEND | ast.RECV: // bidirectional
|
|
310
|
+
c.tsw.WriteLiterally("'both'")
|
|
311
|
+
default:
|
|
312
|
+
// This should not happen, but just in case
|
|
313
|
+
c.tsw.WriteLiterally("'both'")
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
c.tsw.WriteLiterally("}")
|
|
317
|
+
case *ast.FuncType:
|
|
318
|
+
// For function types, create a type descriptor object with params and results
|
|
319
|
+
c.tsw.WriteLiterally("{")
|
|
320
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Function")
|
|
321
|
+
|
|
322
|
+
// Add name if this is a named function type
|
|
323
|
+
if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
|
|
324
|
+
if named, ok := namedType.(*types.Named); ok {
|
|
325
|
+
c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Add params if present
|
|
330
|
+
if typeExpr.Params != nil && len(typeExpr.Params.List) > 0 {
|
|
331
|
+
c.tsw.WriteLiterally(", params: [")
|
|
332
|
+
for i, param := range typeExpr.Params.List {
|
|
333
|
+
if i > 0 {
|
|
334
|
+
c.tsw.WriteLiterally(", ")
|
|
335
|
+
}
|
|
336
|
+
c.writeTypeDescription(param.Type)
|
|
337
|
+
}
|
|
338
|
+
c.tsw.WriteLiterally("]")
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Add results if present
|
|
342
|
+
if typeExpr.Results != nil && len(typeExpr.Results.List) > 0 {
|
|
343
|
+
c.tsw.WriteLiterally(", results: [")
|
|
344
|
+
for i, result := range typeExpr.Results.List {
|
|
345
|
+
if i > 0 {
|
|
346
|
+
c.tsw.WriteLiterally(", ")
|
|
347
|
+
}
|
|
348
|
+
c.writeTypeDescription(result.Type)
|
|
349
|
+
}
|
|
350
|
+
c.tsw.WriteLiterally("]")
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
c.tsw.WriteLiterally("}")
|
|
354
|
+
case *ast.Ident:
|
|
355
|
+
if isPrimitiveType(typeExpr.Name) {
|
|
356
|
+
c.tsw.WriteLiterally("{")
|
|
357
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
|
|
358
|
+
|
|
359
|
+
// Use TypeScript equivalent if available
|
|
360
|
+
if tsType, ok := GoBuiltinToTypescript(typeExpr.Name); ok {
|
|
361
|
+
c.tsw.WriteLiterallyf("name: '%s'", tsType) // TODO: use %q?
|
|
362
|
+
} else {
|
|
363
|
+
c.tsw.WriteLiterallyf("name: '%s'", typeExpr.Name)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
c.tsw.WriteLiterally("}")
|
|
367
|
+
} else {
|
|
368
|
+
// Check if this is a named function type
|
|
369
|
+
isFunctionType := false
|
|
370
|
+
if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
|
|
371
|
+
if named, ok := namedType.(*types.Named); ok {
|
|
372
|
+
if _, ok := named.Underlying().(*types.Signature); ok {
|
|
373
|
+
// This is a named function type, generate a FunctionTypeInfo
|
|
374
|
+
isFunctionType = true
|
|
375
|
+
c.tsw.WriteLiterally("{")
|
|
376
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Function")
|
|
377
|
+
c.tsw.WriteLiterallyf(", name: '%s'", typeExpr.Name)
|
|
378
|
+
|
|
379
|
+
// Get the underlying signature
|
|
380
|
+
signature := named.Underlying().(*types.Signature)
|
|
381
|
+
|
|
382
|
+
// Add params if present
|
|
383
|
+
params := signature.Params()
|
|
384
|
+
if params != nil && params.Len() > 0 {
|
|
385
|
+
c.tsw.WriteLiterally(", params: [")
|
|
386
|
+
for i := 0; i < params.Len(); i++ {
|
|
387
|
+
if i > 0 {
|
|
388
|
+
c.tsw.WriteLiterally(", ")
|
|
389
|
+
}
|
|
390
|
+
// Use basic type info for parameters
|
|
391
|
+
param := params.At(i)
|
|
392
|
+
c.tsw.WriteLiterally("{")
|
|
393
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
|
|
394
|
+
|
|
395
|
+
typeName := param.Type().String()
|
|
396
|
+
if tsType, ok := GoBuiltinToTypescript(typeName); ok {
|
|
397
|
+
c.tsw.WriteLiterallyf("name: '%s'", tsType)
|
|
398
|
+
} else {
|
|
399
|
+
c.tsw.WriteLiterallyf("name: '%s'", typeName)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
c.tsw.WriteLiterally("}")
|
|
403
|
+
}
|
|
404
|
+
c.tsw.WriteLiterally("]")
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Add results if present
|
|
408
|
+
results := signature.Results()
|
|
409
|
+
if results != nil && results.Len() > 0 {
|
|
410
|
+
c.tsw.WriteLiterally(", results: [")
|
|
411
|
+
for i := 0; i < results.Len(); i++ {
|
|
412
|
+
if i > 0 {
|
|
413
|
+
c.tsw.WriteLiterally(", ")
|
|
414
|
+
}
|
|
415
|
+
result := results.At(i)
|
|
416
|
+
c.tsw.WriteLiterally("{")
|
|
417
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
|
|
418
|
+
|
|
419
|
+
typeName := result.Type().String()
|
|
420
|
+
if tsType, ok := GoBuiltinToTypescript(typeName); ok {
|
|
421
|
+
c.tsw.WriteLiterallyf("name: '%s'", tsType)
|
|
422
|
+
} else {
|
|
423
|
+
c.tsw.WriteLiterallyf("name: '%s'", typeName)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
c.tsw.WriteLiterally("}")
|
|
427
|
+
}
|
|
428
|
+
c.tsw.WriteLiterally("]")
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
c.tsw.WriteLiterally("}")
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Default case for non-function named types
|
|
437
|
+
if !isFunctionType {
|
|
438
|
+
c.tsw.WriteLiterallyf("'%s'", typeExpr.Name)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
case *ast.SelectorExpr:
|
|
442
|
+
// For imported types like pkg.Type
|
|
443
|
+
if ident, ok := typeExpr.X.(*ast.Ident); ok {
|
|
444
|
+
c.tsw.WriteLiterallyf("'%s.%s'", ident.Name, typeExpr.Sel.Name)
|
|
445
|
+
} else {
|
|
446
|
+
c.tsw.WriteLiterallyf("'%s'", c.getTypeNameString(assertedType))
|
|
447
|
+
}
|
|
448
|
+
default:
|
|
449
|
+
// For other types, use the string name as before
|
|
450
|
+
typeName := c.getTypeNameString(assertedType)
|
|
451
|
+
c.tsw.WriteLiterallyf("'%s'", typeName)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
c.tsw.WriteLiterally(")")
|
|
455
|
+
|
|
456
|
+
if tok != token.DEFINE || writeEndParen {
|
|
457
|
+
c.tsw.WriteLiterally(")")
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
c.tsw.WriteLine("") // Add newline after the statement
|
|
461
|
+
|
|
462
|
+
return nil
|
|
463
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import "go/types"
|
|
4
|
+
|
|
5
|
+
// writeTypeInfoObject writes a TypeScript TypeInfo object literal for a given Go type.
|
|
6
|
+
func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
|
|
7
|
+
if typ == nil {
|
|
8
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Basic, name: 'any' }") // Or handle as error
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// If typ is a *types.Named, handle it by reference to break recursion.
|
|
13
|
+
if namedType, ok := typ.(*types.Named); ok {
|
|
14
|
+
if namedType.Obj().Name() == "error" && namedType.Obj().Pkg() == nil { // Check for builtin error
|
|
15
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] }")
|
|
16
|
+
} else {
|
|
17
|
+
// For all other named types, output their name as a string literal.
|
|
18
|
+
// This relies on the type being registered elsewhere (e.g., via registerStructType or registerInterfaceType)
|
|
19
|
+
// so the TypeScript runtime can resolve the reference.
|
|
20
|
+
c.tsw.WriteLiterallyf("%q", namedType.Obj().Name())
|
|
21
|
+
}
|
|
22
|
+
return // Return after handling the named type by reference.
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// If typ is not *types.Named, process its underlying structure.
|
|
26
|
+
underlying := typ.Underlying()
|
|
27
|
+
switch t := underlying.(type) {
|
|
28
|
+
case *types.Basic:
|
|
29
|
+
tsTypeName, _ := GoBuiltinToTypescript(t.Name())
|
|
30
|
+
if tsTypeName == "" {
|
|
31
|
+
tsTypeName = t.Name() // Fallback
|
|
32
|
+
}
|
|
33
|
+
c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", tsTypeName)
|
|
34
|
+
// Note: The original 'case *types.Named:' here for 'underlying' is intentionally omitted.
|
|
35
|
+
// If typ.Underlying() is *types.Named (e.g. type T1 MyInt; type T2 T1;),
|
|
36
|
+
// then writeTypeInfoObject(typ.Underlying()) would be called in some contexts,
|
|
37
|
+
// and that call would handle it via the top-level *types.Named check.
|
|
38
|
+
case *types.Pointer:
|
|
39
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Pointer, elemType: ")
|
|
40
|
+
c.writeTypeInfoObject(t.Elem()) // Recursive call
|
|
41
|
+
c.tsw.WriteLiterally(" }")
|
|
42
|
+
case *types.Slice:
|
|
43
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Slice, elemType: ")
|
|
44
|
+
c.writeTypeInfoObject(t.Elem()) // Recursive call
|
|
45
|
+
c.tsw.WriteLiterally(" }")
|
|
46
|
+
case *types.Array:
|
|
47
|
+
c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Array, length: %d, elemType: ", t.Len())
|
|
48
|
+
c.writeTypeInfoObject(t.Elem()) // Recursive call
|
|
49
|
+
c.tsw.WriteLiterally(" }")
|
|
50
|
+
case *types.Map:
|
|
51
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Map, keyType: ")
|
|
52
|
+
c.writeTypeInfoObject(t.Key()) // Recursive call
|
|
53
|
+
c.tsw.WriteLiterally(", elemType: ")
|
|
54
|
+
c.writeTypeInfoObject(t.Elem()) // Recursive call
|
|
55
|
+
c.tsw.WriteLiterally(" }")
|
|
56
|
+
case *types.Chan:
|
|
57
|
+
dir := "both"
|
|
58
|
+
if t.Dir() == types.SendOnly {
|
|
59
|
+
dir = "send"
|
|
60
|
+
} else if t.Dir() == types.RecvOnly {
|
|
61
|
+
dir = "receive"
|
|
62
|
+
}
|
|
63
|
+
c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Channel, direction: %q, elemType: ", dir)
|
|
64
|
+
c.writeTypeInfoObject(t.Elem()) // Recursive call
|
|
65
|
+
c.tsw.WriteLiterally(" }")
|
|
66
|
+
case *types.Interface: // Anonymous interface or underlying of a non-named type alias
|
|
67
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Interface, methods: [")
|
|
68
|
+
var methods []*types.Func
|
|
69
|
+
for i := 0; i < t.NumExplicitMethods(); i++ {
|
|
70
|
+
methods = append(methods, t.ExplicitMethod(i))
|
|
71
|
+
}
|
|
72
|
+
// TODO: Handle embedded methods for anonymous interfaces if needed.
|
|
73
|
+
c.writeMethodSignatures(methods) // Calls writeMethodSignatures -> writeTypeInfoObject
|
|
74
|
+
c.tsw.WriteLiterally("] }")
|
|
75
|
+
case *types.Signature: // Anonymous func type or underlying of a non-named type alias
|
|
76
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Function, params: [")
|
|
77
|
+
for i := 0; i < t.Params().Len(); i++ {
|
|
78
|
+
if i > 0 {
|
|
79
|
+
c.tsw.WriteLiterally(", ")
|
|
80
|
+
}
|
|
81
|
+
c.writeTypeInfoObject(t.Params().At(i).Type()) // Recursive call
|
|
82
|
+
}
|
|
83
|
+
c.tsw.WriteLiterally("], results: [")
|
|
84
|
+
for i := 0; i < t.Results().Len(); i++ {
|
|
85
|
+
if i > 0 {
|
|
86
|
+
c.tsw.WriteLiterally(", ")
|
|
87
|
+
}
|
|
88
|
+
c.writeTypeInfoObject(t.Results().At(i).Type()) // Recursive call
|
|
89
|
+
}
|
|
90
|
+
c.tsw.WriteLiterally("] }")
|
|
91
|
+
case *types.Struct: // Anonymous struct or underlying of a non-named type alias
|
|
92
|
+
c.tsw.WriteLiterally("{ kind: $.TypeKind.Struct, fields: {")
|
|
93
|
+
for i := 0; i < t.NumFields(); i++ {
|
|
94
|
+
if i > 0 {
|
|
95
|
+
c.tsw.WriteLiterally(", ")
|
|
96
|
+
}
|
|
97
|
+
field := t.Field(i)
|
|
98
|
+
c.tsw.WriteLiterallyf("%q: ", field.Name())
|
|
99
|
+
c.writeTypeInfoObject(field.Type()) // Recursive call
|
|
100
|
+
}
|
|
101
|
+
c.tsw.WriteLiterally("}, methods: [] }") // Anonymous structs don't have methods in this context
|
|
102
|
+
default:
|
|
103
|
+
// Fallback, e.g. for types whose underlying isn't one of the above like *types.Tuple or other complex cases.
|
|
104
|
+
c.tsw.WriteLiterallyf("{ kind: $.TypeKind.Basic, name: %q }", typ.String()) // Fallback using the type's string representation
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// writeMethodSignatures writes an array of TypeScript MethodSignature objects.
|
|
109
|
+
func (c *GoToTSCompiler) writeMethodSignatures(methods []*types.Func) {
|
|
110
|
+
firstMethod := true
|
|
111
|
+
for _, method := range methods {
|
|
112
|
+
if !firstMethod {
|
|
113
|
+
c.tsw.WriteLiterally(", ")
|
|
114
|
+
}
|
|
115
|
+
firstMethod = false
|
|
116
|
+
|
|
117
|
+
sig := method.Type().(*types.Signature)
|
|
118
|
+
c.tsw.WriteLiterallyf("{ name: %q, args: [", method.Name())
|
|
119
|
+
for i := 0; i < sig.Params().Len(); i++ {
|
|
120
|
+
if i > 0 {
|
|
121
|
+
c.tsw.WriteLiterally(", ")
|
|
122
|
+
}
|
|
123
|
+
param := sig.Params().At(i)
|
|
124
|
+
c.tsw.WriteLiterallyf("{ name: %q, type: ", param.Name())
|
|
125
|
+
c.writeTypeInfoObject(param.Type())
|
|
126
|
+
c.tsw.WriteLiterally(" }")
|
|
127
|
+
}
|
|
128
|
+
c.tsw.WriteLiterally("], returns: [")
|
|
129
|
+
for i := 0; i < sig.Results().Len(); i++ {
|
|
130
|
+
if i > 0 {
|
|
131
|
+
c.tsw.WriteLiterally(", ")
|
|
132
|
+
}
|
|
133
|
+
result := sig.Results().At(i)
|
|
134
|
+
// Return parameters in Go often don't have names that are relevant for TS signature matching
|
|
135
|
+
c.tsw.WriteLiterally("{ type: ")
|
|
136
|
+
c.writeTypeInfoObject(result.Type())
|
|
137
|
+
c.tsw.WriteLiterally(" }")
|
|
138
|
+
}
|
|
139
|
+
c.tsw.WriteLiterally("] }")
|
|
140
|
+
}
|
|
141
|
+
}
|