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.
@@ -0,0 +1,356 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+ "go/types"
8
+
9
+ "github.com/pkg/errors"
10
+ )
11
+
12
+ // WriteIndexExpr translates a Go index expression (a[b]) to its TypeScript equivalent.
13
+ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
14
+ // Handle map access: use Map.get() instead of brackets for reading values
15
+ if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
16
+ // Check if it's a map type
17
+ if mapType, isMap := tv.Type.Underlying().(*types.Map); isMap {
18
+ c.tsw.WriteLiterally("$.mapGet(")
19
+ if err := c.WriteValueExpr(exp.X); err != nil {
20
+ return err
21
+ }
22
+ c.tsw.WriteLiterally(", ")
23
+ if err := c.WriteValueExpr(exp.Index); err != nil {
24
+ return err
25
+ }
26
+ c.tsw.WriteLiterally(", ")
27
+
28
+ // Generate the zero value as the default value for mapGet
29
+ c.WriteZeroValueForType(mapType.Elem())
30
+ c.tsw.WriteLiterally(")")
31
+ return nil
32
+ }
33
+ }
34
+
35
+ // Regular array/slice access: use brackets
36
+ if err := c.WriteValueExpr(exp.X); err != nil {
37
+ return err
38
+ }
39
+ c.tsw.WriteLiterally("![") // non-null assertion
40
+ if err := c.WriteValueExpr(exp.Index); err != nil {
41
+ return err
42
+ }
43
+ c.tsw.WriteLiterally("]")
44
+ return nil
45
+ }
46
+
47
+ // WriteTypeAssertExpr translates a Go type assertion expression (e.g., `x.(T)`)
48
+ // into a TypeScript call to `$.typeAssert<T_ts>(x_ts, 'TypeName').value`.
49
+ // The `$.typeAssert` runtime function handles the actual type check and panic
50
+ // if the assertion fails. The `.value` access is used because in an expression
51
+ // context, we expect the asserted value directly. The `TypeName` string is used
52
+ // by the runtime for error messages.
53
+ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
54
+ // Generate a call to $.typeAssert
55
+ c.tsw.WriteLiterally("$.typeAssert<")
56
+ c.WriteTypeExpr(exp.Type) // Write the asserted type for the generic
57
+ c.tsw.WriteLiterally(">(")
58
+ if err := c.WriteValueExpr(exp.X); err != nil { // The interface expression
59
+ return fmt.Errorf("failed to write interface expression in type assertion expression: %w", err)
60
+ }
61
+ c.tsw.WriteLiterally(", ")
62
+
63
+ // Write the type description instead of just the type name
64
+ // This ensures we generate proper type info objects for all types
65
+
66
+ // Unwrap parenthesized expressions to handle cases like r.((<-chan T))
67
+ typeExpr := exp.Type
68
+ for {
69
+ if parenExpr, ok := typeExpr.(*ast.ParenExpr); ok {
70
+ typeExpr = parenExpr.X
71
+ } else {
72
+ break
73
+ }
74
+ }
75
+
76
+ c.writeTypeDescription(typeExpr)
77
+
78
+ c.tsw.WriteLiterally(")") // Just close the parenthesis, don't access .value directly
79
+ return nil
80
+ }
81
+
82
+ // isPointerComparison checks if a binary expression `exp` involves comparing
83
+ // two pointer types. It uses `go/types` information to determine the types
84
+ // of the left (X) and right (Y) operands of the binary expression.
85
+ // Returns `true` if both operands are determined to be pointer types,
86
+ // `false` otherwise. This is used to apply specific comparison semantics
87
+ // for pointers (e.g., comparing the box objects directly).
88
+ func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
89
+ leftType := c.pkg.TypesInfo.TypeOf(exp.X)
90
+ rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
91
+ if leftType != nil && rightType != nil {
92
+ if _, leftIsPtr := leftType.(*types.Pointer); leftIsPtr {
93
+ if _, rightIsPtr := rightType.(*types.Pointer); rightIsPtr {
94
+ return true
95
+ }
96
+ }
97
+ }
98
+ return false
99
+ }
100
+
101
+ // getTypeNameString returns a string representation of a Go type expression (`ast.Expr`).
102
+ // It handles simple identifiers (e.g., `MyType`) and selector expressions
103
+ // (e.g., `pkg.Type`). For more complex or unrecognized type expressions,
104
+ // it returns "unknown". This string is primarily used for runtime error messages,
105
+ // such as in type assertions.
106
+ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
107
+ switch t := typeExpr.(type) {
108
+ case *ast.Ident:
109
+ return t.Name
110
+ case *ast.SelectorExpr:
111
+ // For imported types like pkg.Type
112
+ if ident, ok := t.X.(*ast.Ident); ok {
113
+ return fmt.Sprintf("%s.%s", ident.Name, t.Sel.Name)
114
+ }
115
+ }
116
+ // Default case, use a placeholder for complex types
117
+ return "unknown"
118
+ }
119
+
120
+ // WriteBinaryExpr translates a Go binary expression (`ast.BinaryExpr`) into its
121
+ // TypeScript equivalent.
122
+ // It handles several cases:
123
+ // - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
124
+ // - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
125
+ // pointer (which may be a box object or `null`) directly to `null` using
126
+ // the translated operator (`==` or `!=`).
127
+ // - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
128
+ // the box objects directly using strict equality (`===` or `!==`).
129
+ // - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
130
+ // in parentheses `()` to ensure correct precedence in TypeScript, and operators
131
+ // are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
132
+ // - Other binary operations (arithmetic, logical, comparison): Operands are
133
+ // translated using `WriteValueExpr`, and the operator is mapped to its TypeScript
134
+ // equivalent using `TokenToTs`.
135
+ //
136
+ // Unhandled operators result in a comment and a placeholder.
137
+ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
138
+ // Handle special cases like channel send
139
+ if exp.Op == token.ARROW {
140
+ // Channel send: ch <- val becomes await ch.send(val)
141
+ c.tsw.WriteLiterally("await ")
142
+ if err := c.WriteValueExpr(exp.X); err != nil {
143
+ return fmt.Errorf("failed to write channel send target: %w", err)
144
+ }
145
+ c.tsw.WriteLiterally(".send(")
146
+ if err := c.WriteValueExpr(exp.Y); err != nil {
147
+ return fmt.Errorf("failed to write channel send value: %w", err)
148
+ }
149
+ c.tsw.WriteLiterally(")")
150
+ return nil
151
+ }
152
+
153
+ // Check if this is a nil comparison for a pointer
154
+ isNilComparison := false
155
+ var ptrExpr ast.Expr
156
+ if (exp.Op == token.EQL || exp.Op == token.NEQ) && c.pkg != nil && c.pkg.TypesInfo != nil {
157
+ if leftIdent, ok := exp.Y.(*ast.Ident); ok && leftIdent.Name == "nil" {
158
+ leftType := c.pkg.TypesInfo.TypeOf(exp.X)
159
+ if _, isPtr := leftType.(*types.Pointer); isPtr {
160
+ isNilComparison = true
161
+ ptrExpr = exp.X
162
+ }
163
+ } else if rightIdent, ok := exp.X.(*ast.Ident); ok && rightIdent.Name == "nil" {
164
+ rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
165
+ if _, isPtr := rightType.(*types.Pointer); isPtr {
166
+ isNilComparison = true
167
+ ptrExpr = exp.Y
168
+ }
169
+ }
170
+ }
171
+
172
+ if isNilComparison {
173
+ // Compare the box object directly to null
174
+ if err := c.WriteValueExpr(ptrExpr); err != nil {
175
+ return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
176
+ }
177
+ c.tsw.WriteLiterally(" ")
178
+ tokStr, ok := TokenToTs(exp.Op)
179
+ if !ok {
180
+ return errors.Errorf("unhandled binary op: %s", exp.Op.String())
181
+ }
182
+ c.tsw.WriteLiterally(tokStr)
183
+ c.tsw.WriteLiterally(" null")
184
+ return nil
185
+ }
186
+
187
+ // Check if this is a pointer comparison (non-nil)
188
+ // Compare the box objects directly using === or !==
189
+ if c.isPointerComparison(exp) {
190
+ c.tsw.WriteLiterally("(") // Wrap comparison
191
+ if err := c.WriteValueExpr(exp.X); err != nil {
192
+ return fmt.Errorf("failed to write binary expression left operand: %w", err)
193
+ }
194
+ c.tsw.WriteLiterally(" ")
195
+ // Use === for == and !== for !=
196
+ tokStr := ""
197
+ switch exp.Op {
198
+ case token.EQL:
199
+ tokStr = "==="
200
+ case token.NEQ:
201
+ tokStr = "!=="
202
+ default:
203
+ return errors.Errorf("unhandled pointer comparison op: %s", exp.Op.String())
204
+ }
205
+ c.tsw.WriteLiterally(tokStr)
206
+ c.tsw.WriteLiterally(" ")
207
+ if err := c.WriteValueExpr(exp.Y); err != nil {
208
+ return fmt.Errorf("failed to write binary expression right operand: %w", err)
209
+ }
210
+ c.tsw.WriteLiterally(")") // Close wrap
211
+ return nil
212
+ }
213
+
214
+ // Check if the operator is a bitwise operator
215
+ isBitwise := false
216
+ switch exp.Op {
217
+ case token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT:
218
+ isBitwise = true
219
+ }
220
+
221
+ if isBitwise {
222
+ c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
223
+ }
224
+
225
+ if err := c.WriteValueExpr(exp.X); err != nil {
226
+ return fmt.Errorf("failed to write binary expression left operand: %w", err)
227
+ }
228
+ c.tsw.WriteLiterally(" ")
229
+ tokStr, ok := TokenToTs(exp.Op)
230
+ if !ok {
231
+ return errors.Errorf("unhandled binary op: %s", exp.Op.String())
232
+ }
233
+ c.tsw.WriteLiterally(tokStr)
234
+ c.tsw.WriteLiterally(" ")
235
+ if err := c.WriteValueExpr(exp.Y); err != nil {
236
+ return fmt.Errorf("failed to write binary expression right operand: %w", err)
237
+ }
238
+
239
+ if isBitwise {
240
+ c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
241
+ }
242
+
243
+ return nil
244
+ }
245
+
246
+ // WriteUnaryExpr translates a Go unary expression (`ast.UnaryExpr`) into its
247
+ // TypeScript equivalent.
248
+ // It handles several unary operations:
249
+ // - Channel receive (`<-ch`): Becomes `await ch.receive()`.
250
+ // - Address-of (`&var`):
251
+ // - If `var` is a boxed variable (its address was taken), `&var` evaluates
252
+ // to the box itself (i.e., `varName` in TypeScript, which holds the box).
253
+ // - Otherwise (e.g., `&unboxedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
254
+ // the operand `var`. The resulting TypeScript value (e.g., a new object instance)
255
+ // acts as the "pointer". Boxing decisions for such pointers are handled at
256
+ // the assignment site.
257
+ // - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
258
+ // equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
259
+ // around the operand if it's a binary or unary expression to maintain precedence.
260
+ //
261
+ // Unhandled operators result in a comment and an attempt to write the operator
262
+ // token directly. Postfix operators (`++`, `--`) are expected to be handled by
263
+ // their statement contexts (e.g., `IncDecStmt`).
264
+ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
265
+ if exp.Op == token.ARROW {
266
+ // Channel receive: <-ch becomes await ch.receive()
267
+ c.tsw.WriteLiterally("await ")
268
+ if err := c.WriteValueExpr(exp.X); err != nil {
269
+ return fmt.Errorf("failed to write channel receive operand: %w", err)
270
+ }
271
+ c.tsw.WriteLiterally(".receive()")
272
+ return nil
273
+ }
274
+
275
+ if exp.Op == token.AND { // Address-of operator (&)
276
+ // If the operand is an identifier for a variable that is boxed,
277
+ // the result of & is the box itself.
278
+ if ident, ok := exp.X.(*ast.Ident); ok {
279
+ var obj types.Object
280
+ obj = c.pkg.TypesInfo.Uses[ident]
281
+ if obj == nil {
282
+ obj = c.pkg.TypesInfo.Defs[ident]
283
+ }
284
+ if obj != nil && c.analysis.NeedsBoxed(obj) {
285
+ // &boxedVar -> boxedVar (the box itself)
286
+ c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the box)
287
+ return nil
288
+ }
289
+ }
290
+
291
+ // Otherwise (&unboxedVar, &CompositeLit{}, &FuncCall(), etc.),
292
+ // the address-of operator in Go, when used to create a pointer,
293
+ // translates to simply evaluating the operand in TypeScript.
294
+ // The resulting value (e.g., a new object instance) acts as the "pointer".
295
+ // Boxing decisions are handled at the assignment site based on the LHS variable.
296
+ if err := c.WriteValueExpr(exp.X); err != nil {
297
+ return fmt.Errorf("failed to write &-operand: %w", err)
298
+ }
299
+
300
+ return nil
301
+ }
302
+
303
+ // Handle other unary operators (+, -, !, ^)
304
+ tokStr, ok := TokenToTs(exp.Op)
305
+ if !ok {
306
+ return errors.Errorf("unhandled unary op: %s", exp.Op.String())
307
+ }
308
+ c.tsw.WriteLiterally(tokStr)
309
+
310
+ // Add space if operator is not postfix (e.g., !)
311
+ if exp.Op != token.INC && exp.Op != token.DEC {
312
+ // Check if operand needs parentheses (e.g., !(-a))
313
+ // Basic check: if operand is binary or unary, add parens
314
+ needsParens := false
315
+ switch exp.X.(type) {
316
+ case *ast.BinaryExpr, *ast.UnaryExpr:
317
+ needsParens = true
318
+ }
319
+ if needsParens {
320
+ c.tsw.WriteLiterally("(")
321
+ }
322
+ if err := c.WriteValueExpr(exp.X); err != nil {
323
+ return fmt.Errorf("failed to write unary expression operand: %w", err)
324
+ }
325
+ if needsParens {
326
+ c.tsw.WriteLiterally(")")
327
+ }
328
+ } else {
329
+ // Postfix operators (++, --) - operand written first by caller (e.g., IncDecStmt)
330
+ // This function shouldn't be called directly for ++/-- in expression context in valid Go?
331
+ // If it is, write the operand.
332
+ if err := c.WriteValueExpr(exp.X); err != nil {
333
+ return fmt.Errorf("failed to write unary expression operand for postfix op: %w", err)
334
+ }
335
+ }
336
+
337
+ return nil
338
+ }
339
+
340
+ // WriteKeyValueExpr translates a Go key-value pair expression (`ast.KeyValueExpr`),
341
+ // typically found within composite literals (for structs, maps, or arrays with
342
+ // indexed elements), into its TypeScript object property equivalent: `key: value`.
343
+ // Both the key and the value expressions are recursively translated using
344
+ // `WriteValueExpr`. The original Go casing for keys is preserved.
345
+ // For example, `MyField: 123` in Go becomes `MyField: 123` in TypeScript.
346
+ func (c *GoToTSCompiler) WriteKeyValueExpr(exp *ast.KeyValueExpr) error {
347
+ // Keep original Go casing for keys
348
+ if err := c.WriteValueExpr(exp.Key); err != nil {
349
+ return fmt.Errorf("failed to write key-value expression key: %w", err)
350
+ }
351
+ c.tsw.WriteLiterally(": ")
352
+ if err := c.WriteValueExpr(exp.Value); err != nil {
353
+ return fmt.Errorf("failed to write key-value expression value: %w", err)
354
+ }
355
+ return nil
356
+ }
@@ -0,0 +1,169 @@
1
+ package compiler
2
+
3
+ import "go/ast"
4
+
5
+ // WriteFieldList translates a Go field list (`ast.FieldList`), which can represent
6
+ // function parameters, function results, or struct fields, into its TypeScript equivalent.
7
+ // - If `isArguments` is true (for function parameters/results):
8
+ // It iterates through `a.List`, writing each field as `name: type`. Parameter
9
+ // names and types are written using `WriteField` and `WriteGoType` respectively.
10
+ // Multiple parameters are comma-separated.
11
+ // - If `isArguments` is false (for struct fields):
12
+ // It writes an opening brace `{`, indents, then writes each field definition
13
+ // using `WriteField`, followed by a closing brace `}`. If the field list is
14
+ // empty or nil, it simply writes `{}`.
15
+ //
16
+ // This function is a key part of generating TypeScript type signatures for functions
17
+ // and interfaces, as well as struct type definitions.
18
+ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
19
+ if !isArguments && (a == nil || a.NumFields() == 0) {
20
+ c.tsw.WriteLiterally("{}")
21
+ return
22
+ }
23
+
24
+ if !isArguments && a.Opening.IsValid() {
25
+ c.tsw.WriteLine("{")
26
+ c.tsw.Indent(1)
27
+ }
28
+
29
+ // Check if this is a variadic function parameter list
30
+ isVariadic := false
31
+ if isArguments && a != nil && len(a.List) > 0 {
32
+ lastParam := a.List[len(a.List)-1]
33
+ if _, ok := lastParam.Type.(*ast.Ellipsis); ok {
34
+ isVariadic = true
35
+ }
36
+ }
37
+
38
+ if isArguments && isVariadic {
39
+ // Handle non-variadic parameters first
40
+ for i, field := range a.List[:len(a.List)-1] {
41
+ if i > 0 {
42
+ c.tsw.WriteLiterally(", ")
43
+ }
44
+
45
+ // Handle multiple parameter names for the same type
46
+ for j, name := range field.Names {
47
+ if j > 0 {
48
+ c.tsw.WriteLiterally(", ")
49
+ }
50
+ c.tsw.WriteLiterally(name.Name)
51
+ c.tsw.WriteLiterally(": ")
52
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
53
+ c.WriteGoType(typ)
54
+ }
55
+ }
56
+
57
+ // Handle the variadic parameter
58
+ lastParam := a.List[len(a.List)-1]
59
+ if len(a.List) > 1 {
60
+ c.tsw.WriteLiterally(", ")
61
+ }
62
+
63
+ for i, name := range lastParam.Names {
64
+ if i > 0 {
65
+ c.tsw.WriteLiterally(", ")
66
+ }
67
+ c.tsw.WriteLiterally("...")
68
+ c.tsw.WriteLiterally(name.Name)
69
+ }
70
+
71
+ c.tsw.WriteLiterally(": ")
72
+ if ellipsis, ok := lastParam.Type.(*ast.Ellipsis); ok {
73
+ c.WriteTypeExpr(ellipsis.Elt)
74
+ c.tsw.WriteLiterally("[]")
75
+ }
76
+ } else {
77
+ // Handle regular parameter list for function declarations
78
+ for i, field := range a.List {
79
+ if i > 0 && isArguments {
80
+ c.tsw.WriteLiterally(", ")
81
+ }
82
+
83
+ if isArguments {
84
+ // For function parameters with multiple names, write each with its type
85
+ for j, name := range field.Names {
86
+ if j > 0 {
87
+ c.tsw.WriteLiterally(", ")
88
+ }
89
+ c.tsw.WriteLiterally(name.Name)
90
+ c.tsw.WriteLiterally(": ")
91
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
92
+ c.WriteGoType(typ) // Use WriteGoType for parameter type
93
+ }
94
+ } else {
95
+ // For struct fields and other non-argument fields
96
+ c.WriteField(field, false)
97
+ }
98
+ }
99
+ }
100
+
101
+ if !isArguments && a.Closing.IsValid() {
102
+ c.tsw.Indent(-1)
103
+ c.tsw.WriteLine("}")
104
+ }
105
+ }
106
+
107
+ // WriteField translates a single Go field (`ast.Field`) from a field list
108
+ // (e.g., in a struct type or function signature) into its TypeScript representation.
109
+ // - If `isArguments` is false (struct field):
110
+ // - Documentation comments (`field.Doc`, `field.Comment`) are preserved.
111
+ // - If the field is anonymous (embedded), it's skipped as promotions are handled
112
+ // elsewhere (e.g., during struct class generation).
113
+ // - For named fields, it writes `public fieldName: FieldType_ts`. The field name
114
+ // retains its Go casing. The type is translated using `WriteGoType`.
115
+ // - Go struct tags (`field.Tag`) are written as a trailing comment.
116
+ //
117
+ // - If `isArguments` is true (function parameter):
118
+ // - It writes the parameter name (retaining Go casing). The type is handled
119
+ // by the caller (`WriteFieldList`).
120
+ //
121
+ // This function is used by `WriteFieldList` to process individual items within
122
+ // parameter lists and struct field definitions.
123
+ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
124
+ if !isArguments {
125
+ if field.Doc != nil {
126
+ c.WriteDoc(field.Doc)
127
+ }
128
+ if field.Comment != nil {
129
+ c.WriteDoc(field.Comment)
130
+ }
131
+ }
132
+
133
+ // Check if this is an embedded field (anonymous field)
134
+ if len(field.Names) == 0 && !isArguments {
135
+ // This is an embedded field, so we're adding promotions instead of declaring it directly
136
+ return
137
+ }
138
+
139
+ for i, name := range field.Names {
140
+ if i > 0 && isArguments {
141
+ c.tsw.WriteLiterally(", ")
142
+ }
143
+
144
+ // argument names: keep original casing, no access modifier
145
+ if isArguments {
146
+ c.tsw.WriteLiterally(name.Name)
147
+ // Argument type is handled in WriteFieldList, so continue
148
+ continue
149
+ } else {
150
+ // All struct fields are public in TypeScript, keeping original Go casing
151
+ c.tsw.WriteLiterally("public ")
152
+ c.tsw.WriteLiterally(name.Name)
153
+ }
154
+
155
+ // write type for struct fields (not arguments)
156
+ c.tsw.WriteLiterally(": ")
157
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
158
+ c.WriteGoType(typ) // Use WriteGoType for field type
159
+
160
+ if !isArguments {
161
+ // write tag comment if any for struct fields
162
+ if field.Tag != nil {
163
+ c.tsw.WriteCommentLinef("tag: %s", field.Tag.Value)
164
+ } else {
165
+ c.tsw.WriteLine("") // No semicolon
166
+ }
167
+ }
168
+ }
169
+ }
@@ -0,0 +1,99 @@
1
+ package compiler
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/token"
6
+ "strconv"
7
+ )
8
+
9
+ // WriteBasicLit translates a Go basic literal (`ast.BasicLit`) into its
10
+ // TypeScript equivalent.
11
+ // - Character literals (e.g., `'a'`, `'\n'`) are translated to their numeric
12
+ // Unicode code point (e.g., `97`, `10`). Escape sequences are handled.
13
+ // - Integer, float, imaginary, and string literals are written directly as their
14
+ // `exp.Value` string, which typically corresponds to valid TypeScript syntax
15
+ // (e.g., `123`, `3.14`, `"hello"`). Imaginary literals might need special
16
+ // handling if they are to be fully supported beyond direct string output.
17
+ func (c *GoToTSCompiler) WriteBasicLit(exp *ast.BasicLit) {
18
+ if exp.Kind == token.CHAR {
19
+ // Go char literal 'x' is a rune (int32). Translate to its numeric code point.
20
+ // Use strconv.UnquoteChar to handle escape sequences correctly.
21
+ val, _, _, err := strconv.UnquoteChar(exp.Value[1:len(exp.Value)-1], '\'')
22
+ if err != nil {
23
+ c.tsw.WriteCommentInlinef("error parsing char literal %s: %v", exp.Value, err)
24
+ c.tsw.WriteLiterally("0") // Default to 0 on error
25
+ } else {
26
+ c.tsw.WriteLiterallyf("%d", val)
27
+ }
28
+ } else {
29
+ // Other literals (INT, FLOAT, STRING, IMAG)
30
+ c.tsw.WriteLiterally(exp.Value)
31
+ }
32
+ }
33
+
34
+ // WriteFuncLitValue translates a Go function literal (`ast.FuncLit`) into a
35
+ // TypeScript arrow function.
36
+ // The translation results in: `[async] (param1: type1, ...) : returnType => { ...body... }`.
37
+ // - The `async` keyword is prepended if `c.analysis.IsFuncLitAsync(exp)`
38
+ // indicates the function literal contains asynchronous operations.
39
+ // - Parameters are translated using `WriteFieldList`.
40
+ // - The return type is determined similarly to `WriteFuncType`:
41
+ // - `void` for no results.
42
+ // - `resultType` for a single unnamed result.
43
+ // - `[typeA, typeB]` for multiple or named results.
44
+ // - Wrapped in `Promise<>` if `async`.
45
+ // - The function body (`exp.Body`) is translated using `WriteStmt`.
46
+ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
47
+ // Determine if the function literal should be async
48
+ isAsync := c.analysis.IsFuncLitAsync(exp)
49
+
50
+ if isAsync {
51
+ c.tsw.WriteLiterally("async ")
52
+ }
53
+
54
+ // Write arrow function: (params) => { body }
55
+ c.tsw.WriteLiterally("(")
56
+
57
+ // Use WriteFieldList which now handles variadic parameters
58
+ c.WriteFieldList(exp.Type.Params, true) // true = arguments
59
+
60
+ c.tsw.WriteLiterally(")")
61
+
62
+ // Handle return type for function literals
63
+ if exp.Type.Results != nil && len(exp.Type.Results.List) > 0 {
64
+ c.tsw.WriteLiterally(": ")
65
+ if isAsync {
66
+ c.tsw.WriteLiterally("Promise<")
67
+ }
68
+ if len(exp.Type.Results.List) == 1 && len(exp.Type.Results.List[0].Names) == 0 {
69
+ c.WriteTypeExpr(exp.Type.Results.List[0].Type)
70
+ } else {
71
+ c.tsw.WriteLiterally("[")
72
+ for i, field := range exp.Type.Results.List {
73
+ if i > 0 {
74
+ c.tsw.WriteLiterally(", ")
75
+ }
76
+ c.WriteTypeExpr(field.Type)
77
+ }
78
+ c.tsw.WriteLiterally("]")
79
+ }
80
+ if isAsync {
81
+ c.tsw.WriteLiterally(">")
82
+ }
83
+ } else {
84
+ if isAsync {
85
+ c.tsw.WriteLiterally(": Promise<void>")
86
+ } else {
87
+ c.tsw.WriteLiterally(": void")
88
+ }
89
+ }
90
+
91
+ c.tsw.WriteLiterally(" => ")
92
+
93
+ // Write function body
94
+ if err := c.WriteStmt(exp.Body); err != nil {
95
+ return err
96
+ }
97
+
98
+ return nil
99
+ }