typenative 0.0.16 → 0.0.17

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/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to TypeNative will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.17] - 2026-02-16
9
+
10
+ ### Added
11
+
12
+ - Support for ternary expressions (`condition ? a : b`)
13
+ - Support for optional chaining (`obj?.prop`, `arr?.[i]`)
14
+ - Support for nullish coalescing (`??` operator)
15
+ - Support for type aliases (`type X = ...`)
16
+ - Support for type assertions (`expr as Type`)
17
+ - Support for optional properties (`prop?: Type` in interfaces/types)
18
+ - Support for default parameter values (e.g. `function(x = defaultValue)`)
19
+ - Support for enum declarations and member access (`enum X { ... }`, `X.Member`)
20
+ - Support for array methods: `map`, `filter`, `some`, `find`
21
+ - Support for chaining array methods (e.g. `arr.map(...).filter(...).join(...)`)
22
+
8
23
  ## [0.0.16] - 2025-02-15
9
24
 
10
25
  ### Added
package/README.md CHANGED
@@ -11,99 +11,142 @@ Build native applications using Typescript.
11
11
 
12
12
  - Write a file `test.ts` with content `console.log('Hello World!');` or any other message
13
13
  - Run `npx typenative --source test.ts --script`
14
- - See your message in the terminal
15
14
 
16
- ## Create a TypeNative Project
15
+ ## Typescript Syntax Support
17
16
 
18
- - Run `npx typenative --new`
19
- - Give your project a name
20
- - Start writing code
17
+ TypeNative currently supports a focused subset of TypeScript syntax elements that are transpiled to Go code. The support is grouped by topic for easier scanning.
21
18
 
22
- ## Typescript Syntax Support
19
+ **Basic Types**
20
+
21
+ | Feature | Supported | Notes |
22
+ | -------------- | :-------: | ------------------------------------------------------------- |
23
+ | number | ✅ | Transpiled to `float64` |
24
+ | boolean | ✅ | Transpiled to `bool` |
25
+ | string | ✅ | |
26
+ | null | ✅ | |
27
+ | any | ✅ | Used for type inference |
28
+ | Nullable types | ✅ | `T \| null` / `T \| undefined` transpiled to Go pointer types |
29
+
30
+ **Variables & Objects**
31
+
32
+ | Feature | Supported | Notes |
33
+ | --------------------- | :-------: | -------------------------------- |
34
+ | Variable declarations | ✅ | `let` and `const` |
35
+ | Object literals | ✅ | Transpiled to Go struct literals |
36
+
37
+ **Operators**
38
+
39
+ | Feature | Supported | Notes |
40
+ | ------------------------ | :-------: | ------------------------------ |
41
+ | Arithmetic operators | ✅ | `+`, `-`, etc. |
42
+ | Comparison operators | ✅ | `==`, `!=`, `===`, `!==`, etc. |
43
+ | Logical operators | ✅ | `&&`, `\|\|` |
44
+ | Increment/Decrement | ✅ | `++`, `--` |
45
+ | Non-null assertion (`!`) | ✅ | Stripped during transpilation |
46
+ | Ternary expressions | ✅ | `condition ? a : b` |
47
+ | Nullish coalescing | ✅ | `??` operator |
48
+ | Optional chaining | ✅ | `obj?.prop`, `arr?.[i]` |
49
+
50
+ **Control Flow**
51
+
52
+ | Feature | Supported | Notes |
53
+ | ------------------ | :-------: | ---------------------------------- |
54
+ | If/Else statements | ✅ | Fully supported |
55
+ | Switch statements | ✅ | Case and default statements |
56
+ | For loops | ✅ | Standard `for` loops |
57
+ | For...of loops | ✅ | Iteration over arrays |
58
+ | While loops | ✅ | Transpiled to Go's `for` loops |
59
+ | Do...while loops | ✅ | Implemented with conditional break |
60
+
61
+ **Data Structures & Array Methods**
62
+
63
+ | Feature | Supported | Notes |
64
+ | -------------------------- | :-------: | ---------------------------------------------------------------- |
65
+ | Arrays | ✅ | Basic array operations |
66
+ | Array methods | ✅ | `push`, `join`, `slice` |
67
+ | Higher-order array methods | ✅ | `.map()`, `.filter()`, `.some()`, `.find()` |
68
+ | Method chaining | ✅ | Chaining array methods such as `.map(...).filter(...).join(...)` |
69
+
70
+ **Functions**
71
+
72
+ | Feature | Supported | Notes |
73
+ | ---------------------------- | :-------: | --------------------------------- |
74
+ | Function declarations | ✅ | Transpiled to Go functions |
75
+ | Arrow functions | ✅ | Transpiled to anonymous functions |
76
+ | Generics (functions/classes) | ✅ | Type parameters via Go generics |
77
+ | Default parameter values | ✅ | `function(x = defaultValue)` |
78
+
79
+ **Classes & Interfaces**
80
+
81
+ | Feature | Supported | Notes |
82
+ | ------------------- | :-------: | -------------------------------------------------------------- |
83
+ | Classes | ✅ | Transpiled to Go structs with constructor and receiver methods |
84
+ | Class inheritance | ✅ | `extends` via embedded structs, `super()` supported |
85
+ | Interfaces | ✅ | Transpiled to Go interfaces, supports `extends` |
86
+ | Optional properties | ✅ | `prop?: Type` in interfaces/types |
87
+ | Enums | ✅ | `enum` declarations and member access |
88
+
89
+ **Async & Timing**
90
+
91
+ | Feature | Supported | Notes |
92
+ | ----------- | :-------: | ----------------------------------------------------------------- |
93
+ | Async/Await | ✅ | `async` functions return Go channels, `await` reads from channels |
94
+ | Promises | ✅ | `new Promise` transpiled to channel + goroutine pattern |
95
+ | setTimeout | ✅ | Mapped to Go's `time.AfterFunc` |
96
+
97
+ **Built-in Functions & Utilities**
98
+
99
+ | Feature | Supported | Notes |
100
+ | --------------------- | :-------: | ----------------------------------------------------- |
101
+ | console.log | ✅ | Mapped to `fmt.Println` |
102
+ | console.time/timeEnd | ✅ | Performance measurement via `time.Now` / `time.Since` |
103
+ | assert | ✅ | Transpiled to `panic` on failure |
104
+ | parseInt / parseFloat | ✅ | Mapped to Go's `strconv` package |
105
+
106
+ **Math Methods**
107
+
108
+ | Feature | Supported | Notes |
109
+ | ------------------------- | :-------: | ------------------------------------------------- |
110
+ | Math.random | ✅ | Mapped to `rand.Float64()` |
111
+ | Math.floor / ceil / round | ✅ | Mapped to `math.Floor`, `math.Ceil`, `math.Round` |
112
+ | Math.abs / sqrt / pow | ✅ | Mapped to corresponding `math` functions |
113
+ | Math.min / max | ✅ | Mapped to `math.Min`, `math.Max` |
114
+
115
+ **String Methods**
116
+
117
+ | Feature | Supported | Notes |
118
+ | -------------------------- | :-------: | --------------------------------------------- |
119
+ | Template literals | ✅ | Backtick strings with `${expr}` interpolation |
120
+ | toUpperCase / toLowerCase | ✅ | Via `strings` package |
121
+ | trim / trimStart / trimEnd | ✅ | Via `strings` package |
122
+ | split / includes / indexOf | ✅ | Via `strings` package |
123
+ | startsWith / endsWith | ✅ | Via `strings` package |
124
+ | replace / replaceAll | ✅ | Via `strings` package |
125
+ | charAt / substring / slice | ✅ | Direct Go string indexing/slicing |
126
+ | concat / repeat | ✅ | String concatenation and `strings.Repeat` |
127
+
128
+ **Number / Object Methods**
129
+
130
+ | Feature | Supported | Notes |
131
+ | -------- | :-------: | ----------------------------------------------------- |
132
+ | toString | ✅ | Universal `toString()` via `fmt.Sprintf` for any type |
133
+
134
+ **RegExp**
135
+
136
+ | Feature | Supported | Notes |
137
+ | -------------- | :-------: | --------------------------------------------------- |
138
+ | Regex literals | ✅ | `/pattern/flags` transpiled to `regexp.MustCompile` |
139
+ | new RegExp() | ✅ | Constructor with optional flags |
140
+ | test() | ✅ | Mapped to `regexp.MatchString` |
141
+ | exec() | ✅ | Mapped to `regexp.FindStringSubmatch` |
142
+
143
+ **Unsupported / Roadmap**
144
+
145
+ | Feature | Supported | Notes |
146
+ | --------------------------- | :-------: | ------------------------------------------------ |
147
+ | Modules/Imports | ❌ | `import` / `export` declarations |
148
+ | Try/Catch | ❌ | Error handling |
149
+ | Map / Set | ❌ | Built-in collection types and their methods |
150
+ | Closures over mutable state | ❌ | Functions capturing and mutating outer variables |
23
151
 
24
- TypeNative currently supports a subset of TypeScript syntax elements that are transpiled to Go code:
25
-
26
- | Feature | Supported | Notes |
27
- | --------------------------- | :-------: | ----------------------------------------------------------------- |
28
- | **Basic Types** | | |
29
- | number | ✅ | Transpiled to `float64` |
30
- | boolean | ✅ | Transpiled to `bool` |
31
- | string | ✅ | |
32
- | null | ✅ | |
33
- | any | ✅ | Used for type inference |
34
- | Nullable types | ✅ | `T \| null` / `T \| undefined` transpiled to Go pointer types |
35
- | **Variables** | | |
36
- | Variable declarations | ✅ | `let` and `const` |
37
- | Object literals | ✅ | Transpiled to Go struct literals |
38
- | **Operators** | | |
39
- | Arithmetic operators | ✅ | `+`, `-`, etc. |
40
- | Comparison operators | ✅ | `==`, `!=`, `===`, `!==`, etc. |
41
- | Logical operators | ✅ | `&&`, `\|\|` |
42
- | Increment/Decrement | ✅ | `++`, `--` |
43
- | Non-null assertion (`!`) | ✅ | Stripped during transpilation |
44
- | **Control Flow** | | |
45
- | For loops | ✅ | Standard `for` loops |
46
- | For...of loops | ✅ | Iteration over arrays |
47
- | While loops | ✅ | Transpiled to Go's `for` loops |
48
- | Do...while loops | ✅ | Implemented with conditional break |
49
- | If/Else statements | ✅ | Fully supported |
50
- | Switch statements | ✅ | Case and default statements |
51
- | **Data Structures** | | |
52
- | Arrays | ✅ | Basic array operations |
53
- | Array methods | ✅ | `push`, `join`, `slice`, `toString` |
54
- | **Functions** | | |
55
- | Function declarations | ✅ | Transpiled to Go functions |
56
- | Arrow functions | ✅ | Transpiled to anonymous functions |
57
- | **Classes & Interfaces** | | |
58
- | Classes | ✅ | Transpiled to Go structs with constructor and receiver methods |
59
- | Class inheritance | ✅ | `extends` via embedded structs, `super()` supported |
60
- | Interfaces | ✅ | Transpiled to Go interfaces, supports `extends` |
61
- | Generics | ✅ | Type parameters on functions and classes via Go generics |
62
- | **Async** | | |
63
- | Async/Await | ✅ | `async` functions return Go channels, `await` reads from channels |
64
- | Promises | ✅ | `new Promise` transpiled to channel + goroutine pattern |
65
- | setTimeout | ✅ | Mapped to Go's `time.AfterFunc` |
66
- | **Built-in Functions** | | |
67
- | console.log | ✅ | Mapped to `fmt.Println` |
68
- | console.time/timeEnd | ✅ | Performance measurement via `time.Now` / `time.Since` |
69
- | assert | ✅ | Transpiled to `panic` on failure |
70
- | parseInt / parseFloat | ✅ | Mapped to Go's `strconv` package |
71
- | **Math Methods** | | |
72
- | Math.random | ✅ | Mapped to `rand.Float64()` |
73
- | Math.floor / ceil / round | ✅ | Mapped to `math.Floor`, `math.Ceil`, `math.Round` |
74
- | Math.abs / sqrt / pow | ✅ | Mapped to corresponding `math` functions |
75
- | Math.min / max | ✅ | Mapped to `math.Min`, `math.Max` |
76
- | **String Methods** | | |
77
- | toUpperCase / toLowerCase | ✅ | Via `strings` package |
78
- | trim / trimStart / trimEnd | ✅ | Via `strings` package |
79
- | split / includes / indexOf | ✅ | Via `strings` package |
80
- | startsWith / endsWith | ✅ | Via `strings` package |
81
- | replace / replaceAll | ✅ | Via `strings` package |
82
- | charAt / substring / slice | ✅ | Direct Go string indexing/slicing |
83
- | concat / repeat / toString | ✅ | String concatenation and `strings.Repeat` |
84
- | **Number / Object Methods** | | |
85
- | toString | ✅ | Universal `toString()` via `fmt.Sprintf` for any type |
86
- | **RegExp** | | |
87
- | Regex literals | ✅ | `/pattern/flags` transpiled to `regexp.MustCompile` |
88
- | new RegExp() | ✅ | Constructor with optional flags |
89
- | test() | ✅ | Mapped to `regexp.MatchString` |
90
- | exec() | ✅ | Mapped to `regexp.FindStringSubmatch` |
91
- | **Unsupported Features** | | |
92
- | Modules/Imports | ❌ | `import` / `export` declarations |
93
- | Try/Catch | ❌ | Error handling |
94
- | Template literals | ❌ | Backtick strings with `${expr}` interpolation |
95
- | Ternary expressions | ❌ | `condition ? a : b` |
96
- | Optional chaining | ❌ | `obj?.prop`, `arr?.[i]` |
97
- | Nullish coalescing | ❌ | `??` operator |
98
- | Spread operator | ❌ | `[...iterable]`, `{...obj}` |
99
- | Type aliases | ❌ | `type X = ...` |
100
- | Type assertions | ❌ | `expr as Type` |
101
- | Optional properties | ❌ | `prop?: Type` in interfaces/types |
102
- | Default parameter values | ❌ | `function(x = defaultValue)` |
103
- | Map / Set | ❌ | Built-in collection types and their methods |
104
- | Higher-order array methods | ❌ | `.map()`, `.filter()`, `.some()`, `.find()` with callbacks |
105
- | Method chaining | ❌ | `arr.map(...).filter(...).join(...)` |
106
- | Enums | ❌ | `enum` declarations and member access |
107
- | Closures over mutable state | ❌ | Functions capturing and mutating outer variables |
108
-
109
- TypeNative is currently in early development and new features are being added regularly.
152
+ TypeNative is currently in early development and new features are being added regularly. The goal for `1.0` release is for TypeNative to transpile itself.
package/bin/transpiler.js CHANGED
@@ -9,6 +9,14 @@ let promiseResolveName = '';
9
9
  const dangerousNames = new Set(['main']);
10
10
  const renamedFunctions = new Map();
11
11
  const variableTypes = new Map();
12
+ const variableGoTypes = new Map();
13
+ const variableClassNames = new Map();
14
+ const classPropertyTypes = new Map();
15
+ const classMethodReturnTypes = new Map();
16
+ const interfacePropertyTypes = new Map();
17
+ const typeAliases = new Map();
18
+ const enumNames = new Set();
19
+ const enumBaseTypes = new Map();
12
20
  export function transpileToNative(code) {
13
21
  const sourceFile = ts.createSourceFile('main.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
14
22
  TypeCheker = ts.createProgram(['main.ts'], {}).getTypeChecker();
@@ -18,6 +26,14 @@ export function transpileToNative(code) {
18
26
  promiseResolveName = '';
19
27
  renamedFunctions.clear();
20
28
  variableTypes.clear();
29
+ variableGoTypes.clear();
30
+ variableClassNames.clear();
31
+ classPropertyTypes.clear();
32
+ classMethodReturnTypes.clear();
33
+ interfacePropertyTypes.clear();
34
+ typeAliases.clear();
35
+ enumNames.clear();
36
+ enumBaseTypes.clear();
21
37
  const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
22
38
  const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
23
39
  return `package main
@@ -43,8 +59,17 @@ export function visit(node, options = {}) {
43
59
  return 'nil';
44
60
  return getSafeName(node.text);
45
61
  }
46
- else if (ts.isStringLiteral(node)) {
47
- return `"${node.text}"`;
62
+ else if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
63
+ return toGoStringLiteral(node.text);
64
+ }
65
+ else if (ts.isAsExpression(node)) {
66
+ return visit(node.expression);
67
+ }
68
+ else if (ts.isTypeAssertionExpression(node)) {
69
+ return visit(node.expression);
70
+ }
71
+ else if (ts.isTemplateExpression(node)) {
72
+ return visitTemplateExpression(node);
48
73
  }
49
74
  else if (ts.isNumericLiteral(node)) {
50
75
  return `float64(${node.text})`;
@@ -73,12 +98,23 @@ export function visit(node, options = {}) {
73
98
  return `[]${type} {${node.elements.map((e) => visit(e)).join(', ')}}`;
74
99
  }
75
100
  else if (ts.isBlock(node)) {
76
- return `{\n\t\t${node.statements.map((n) => visit(n)).join('\t')}${options.extraBlockContent ?? ''}}${options.inline ? '' : '\n\t'}`;
101
+ return `{\n\t\t${options.prefixBlockContent ?? ''}${node.statements
102
+ .map((n) => visit(n))
103
+ .join('\t')}${options.extraBlockContent ?? ''}}${options.inline ? '' : '\n\t'}`;
77
104
  }
78
105
  else if (ts.isElementAccessExpression(node)) {
106
+ if (hasQuestionDot(node)) {
107
+ return visitOptionalElementAccess(node);
108
+ }
79
109
  return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
80
110
  }
81
111
  else if (ts.isPropertyAccessExpression(node)) {
112
+ if (hasQuestionDot(node)) {
113
+ return visitOptionalPropertyAccess(node);
114
+ }
115
+ if (ts.isIdentifier(node.expression) && enumNames.has(node.expression.text)) {
116
+ return `${getSafeName(node.expression.text)}_${getEnumMemberName(node.name)}`;
117
+ }
82
118
  const leftSide = visit(node.expression);
83
119
  const rightSide = visit(node.name);
84
120
  const objectType = resolveExpressionType(node.expression);
@@ -88,9 +124,18 @@ export function visit(node, options = {}) {
88
124
  const type = getType(node.type);
89
125
  // Track variable type for type-aware method dispatch
90
126
  if (ts.isIdentifier(node.name)) {
127
+ if (node.type) {
128
+ variableGoTypes.set(node.name.text, getType(node.type));
129
+ }
91
130
  const cat = node.type ? getTypeCategory(node.type) : undefined;
92
131
  if (cat) {
93
132
  variableTypes.set(node.name.text, cat);
133
+ if (cat === 'class' && node.type) {
134
+ const className = getClassNameFromTypeNode(node.type);
135
+ if (className) {
136
+ variableClassNames.set(node.name.text, className);
137
+ }
138
+ }
94
139
  }
95
140
  else if (node.initializer &&
96
141
  ts.isNewExpression(node.initializer) &&
@@ -100,11 +145,18 @@ export function visit(node, options = {}) {
100
145
  }
101
146
  else if (classNames.has(node.initializer.expression.text)) {
102
147
  variableTypes.set(node.name.text, 'class');
148
+ variableClassNames.set(node.name.text, node.initializer.expression.text);
103
149
  }
104
150
  }
105
151
  else if (node.initializer && ts.isRegularExpressionLiteral(node.initializer)) {
106
152
  variableTypes.set(node.name.text, 'RegExp');
107
153
  }
154
+ if (!variableGoTypes.has(node.name.text) && node.initializer) {
155
+ const inferredType = inferExpressionType(node.initializer);
156
+ if (inferredType) {
157
+ variableGoTypes.set(node.name.text, inferredType);
158
+ }
159
+ }
108
160
  }
109
161
  let initializer = node.initializer ? `= ${visit(node.initializer)}` : '';
110
162
  // Wrap non-nil values assigned to nullable primitive pointer types
@@ -119,6 +171,10 @@ export function visit(node, options = {}) {
119
171
  return `${type === ':' ? '' : 'var '}${visit(node.name)} ${type}${type === ':' ? '' : ' '}${initializer}`;
120
172
  }
121
173
  else if (ts.isCallExpression(node)) {
174
+ if (hasQuestionDot(node) ||
175
+ (ts.isPropertyAccessExpression(node.expression) && hasQuestionDot(node.expression))) {
176
+ return visitOptionalCall(node);
177
+ }
122
178
  // Handle setTimeout specially to get raw delay value
123
179
  if (ts.isIdentifier(node.expression) && node.expression.text === 'setTimeout') {
124
180
  importedPackages.add('time');
@@ -127,6 +183,10 @@ export function visit(node, options = {}) {
127
183
  const delay = ts.isNumericLiteral(delayNode) ? delayNode.text : visit(delayNode);
128
184
  return `time.AfterFunc(${delay} * time.Millisecond, ${callback.trimEnd()})`;
129
185
  }
186
+ const arrayHigherOrderCall = visitArrayHigherOrderCall(node);
187
+ if (arrayHigherOrderCall) {
188
+ return arrayHigherOrderCall;
189
+ }
130
190
  const caller = visit(node.expression);
131
191
  const safeCaller = getSafeName(caller);
132
192
  const typeArgs = getTypeArguments(node.typeArguments);
@@ -144,7 +204,13 @@ export function visit(node, options = {}) {
144
204
  else if (ts.isPostfixUnaryExpression(node)) {
145
205
  return `${visit(node.operand, { inline: true })}${getOperatorText(node.operator)}`;
146
206
  }
207
+ else if (ts.isConditionalExpression(node)) {
208
+ return visitConditionalExpression(node);
209
+ }
147
210
  else if (ts.isBinaryExpression(node)) {
211
+ if (node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
212
+ return visitNullishCoalescingExpression(node);
213
+ }
148
214
  let op = node.operatorToken.getText();
149
215
  if (op === '===')
150
216
  op = '==';
@@ -233,28 +299,56 @@ export function visit(node, options = {}) {
233
299
  const name = visit(node.name, { inline: true });
234
300
  const safeName = getSafeName(name);
235
301
  const typeParams = getTypeParameters(node.typeParameters);
236
- const parameters = node.parameters
237
- .map((p) => `${visit(p.name)} ${getType(p.type)}`)
238
- .join(', ');
302
+ const parameterInfo = getFunctionParametersInfo(node.parameters);
239
303
  const returnType = node.type ? ` ${getType(node.type)}` : '';
240
304
  if (options.isOutside) {
241
- return `func ${safeName}${typeParams}(${parameters})${returnType} ${visit(node.body)}`;
305
+ return `func ${safeName}${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
306
+ prefixBlockContent: parameterInfo.prefixBlockContent
307
+ })}`;
242
308
  }
243
- return `${safeName} := func${typeParams}(${parameters})${returnType} ${visit(node.body)}`;
309
+ return `${safeName} := func${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
310
+ prefixBlockContent: parameterInfo.prefixBlockContent
311
+ })}`;
244
312
  }
245
313
  else if (ts.isArrowFunction(node)) {
246
- const parameters = node.parameters
247
- .map((p) => `${visit(p.name)} ${getType(p.type)}`)
248
- .join(', ');
314
+ const parameterInfo = getFunctionParametersInfo(node.parameters);
249
315
  const returnType = node.type ? ` ${getType(node.type)}` : '';
250
- return `func(${parameters})${returnType} ${visit(node.body)}`;
316
+ if (parameterInfo.prefixBlockContent && !ts.isBlock(node.body)) {
317
+ return `func(${parameterInfo.signature})${returnType} {\n\t\t${parameterInfo.prefixBlockContent}return ${visit(node.body)};\n\t}`;
318
+ }
319
+ return `func(${parameterInfo.signature})${returnType} ${visit(node.body, {
320
+ prefixBlockContent: parameterInfo.prefixBlockContent
321
+ })}`;
251
322
  }
252
323
  else if (node.kind === ts.SyntaxKind.ThisKeyword) {
253
324
  return 'self';
254
325
  }
326
+ else if (ts.isEnumDeclaration(node)) {
327
+ const enumName = node.name.text;
328
+ enumNames.add(enumName);
329
+ enumBaseTypes.set(enumName, getEnumBaseType(node));
330
+ if (options.addFunctionOutside) {
331
+ outsideNodes.push(node);
332
+ return '';
333
+ }
334
+ return visitEnumDeclaration(node);
335
+ }
336
+ else if (ts.isTypeAliasDeclaration(node)) {
337
+ typeAliases.set(node.name.text, node.type);
338
+ return '';
339
+ }
255
340
  else if (ts.isInterfaceDeclaration(node)) {
256
341
  if (options.addFunctionOutside) {
257
342
  outsideNodes.push(node);
343
+ const properties = new Map();
344
+ for (const member of node.members) {
345
+ if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
346
+ properties.set(member.name.text, getOptionalNodeType(member.type, !!member.questionToken));
347
+ }
348
+ }
349
+ if (properties.size > 0) {
350
+ interfacePropertyTypes.set(visit(node.name), properties);
351
+ }
258
352
  return '';
259
353
  }
260
354
  const name = visit(node.name);
@@ -270,6 +364,7 @@ export function visit(node, options = {}) {
270
364
  }
271
365
  }
272
366
  const methods = [];
367
+ const properties = [];
273
368
  for (const member of node.members) {
274
369
  if (ts.isMethodSignature(member)) {
275
370
  const methodName = visit(member.name);
@@ -279,6 +374,13 @@ export function visit(node, options = {}) {
279
374
  const returnType = member.type ? ` ${getType(member.type)}` : '';
280
375
  methods.push(`\t${methodName}(${params})${returnType}`);
281
376
  }
377
+ else if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
378
+ properties.push(`\t${member.name.text} ${getOptionalNodeType(member.type, !!member.questionToken)}`);
379
+ }
380
+ }
381
+ if (properties.length > 0 && methods.length === 0) {
382
+ const fields = [...extendedInterfaces.map((e) => `\t${e}`), ...properties];
383
+ return `type ${name}${typeParams} struct {\n${fields.join('\n')}\n}`;
282
384
  }
283
385
  const members = [...extendedInterfaces.map((e) => `\t${e}`), ...methods];
284
386
  return `type ${name}${typeParams} interface {\n${members.join('\n')}\n}`;
@@ -286,7 +388,20 @@ export function visit(node, options = {}) {
286
388
  else if (ts.isClassDeclaration(node)) {
287
389
  if (options.addFunctionOutside) {
288
390
  outsideNodes.push(node);
289
- classNames.add(visit(node.name));
391
+ const className = visit(node.name);
392
+ classNames.add(className);
393
+ const properties = new Map();
394
+ const methods = new Map();
395
+ for (const member of node.members) {
396
+ if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name)) {
397
+ properties.set(member.name.text, getOptionalNodeType(member.type, !!member.questionToken));
398
+ }
399
+ if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name)) {
400
+ methods.set(member.name.text, member.type ? getType(member.type) : 'interface{}');
401
+ }
402
+ }
403
+ classPropertyTypes.set(className, properties);
404
+ classMethodReturnTypes.set(className, methods);
290
405
  return '';
291
406
  }
292
407
  const name = visit(node.name);
@@ -312,7 +427,7 @@ export function visit(node, options = {}) {
312
427
  fieldType = `[]${getType(member.type, true)}`;
313
428
  }
314
429
  else {
315
- fieldType = member.type ? getType(member.type) : 'interface{}';
430
+ fieldType = getOptionalNodeType(member.type, !!member.questionToken);
316
431
  }
317
432
  fields.push(`\t${fieldName} ${fieldType}`);
318
433
  }
@@ -320,7 +435,7 @@ export function visit(node, options = {}) {
320
435
  let result = `type ${name}${typeParams} struct {\n${fields.join('\n')}\n}\n\n`;
321
436
  const ctor = node.members.find((m) => ts.isConstructorDeclaration(m));
322
437
  if (ctor) {
323
- const params = ctor.parameters.map((p) => `${visit(p.name)} ${getType(p.type)}`).join(', ');
438
+ const ctorParameterInfo = getFunctionParametersInfo(ctor.parameters);
324
439
  const bodyStatements = ctor.body?.statements
325
440
  .filter((s) => {
326
441
  if (ts.isExpressionStatement(s) && ts.isCallExpression(s.expression)) {
@@ -330,7 +445,7 @@ export function visit(node, options = {}) {
330
445
  })
331
446
  .map((s) => visit(s))
332
447
  .join('\t') ?? '';
333
- result += `func New${name}${typeParams}(${params}) *${name}${typeParamNames} {\n\t\tself := &${name}${typeParamNames}{}\n\t\t${bodyStatements}return self;\n\t}\n\n`;
448
+ result += `func New${name}${typeParams}(${ctorParameterInfo.signature}) *${name}${typeParamNames} {\n\t\tself := &${name}${typeParamNames}{}\n\t\t${ctorParameterInfo.prefixBlockContent}${bodyStatements}return self;\n\t}\n\n`;
334
449
  }
335
450
  else {
336
451
  result += `func New${name}${typeParams}() *${name}${typeParamNames} {\n\t\treturn &${name}${typeParamNames}{}\n\t}\n\n`;
@@ -338,11 +453,11 @@ export function visit(node, options = {}) {
338
453
  for (const member of node.members) {
339
454
  if (ts.isMethodDeclaration(member)) {
340
455
  const methodName = visit(member.name);
341
- const params = member.parameters
342
- .map((p) => `${visit(p.name)} ${getType(p.type)}`)
343
- .join(', ');
456
+ const methodParameterInfo = getFunctionParametersInfo(member.parameters);
344
457
  const returnType = member.type ? ` ${getType(member.type)}` : '';
345
- result += `func (self *${name}${typeParamNames}) ${methodName}(${params})${returnType} ${visit(member.body)}\n\n`;
458
+ result += `func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, {
459
+ prefixBlockContent: methodParameterInfo.prefixBlockContent
460
+ })}\n\n`;
346
461
  }
347
462
  }
348
463
  return result.trim();
@@ -408,9 +523,492 @@ function getTypeText(typeNode) {
408
523
  }
409
524
  return getType(typeNode);
410
525
  }
526
+ function toGoStringLiteral(value) {
527
+ return JSON.stringify(value);
528
+ }
529
+ function visitTemplateExpression(node) {
530
+ const parts = [];
531
+ if (node.head.text.length > 0) {
532
+ parts.push(toGoStringLiteral(node.head.text));
533
+ }
534
+ for (const span of node.templateSpans) {
535
+ importedPackages.add('fmt');
536
+ parts.push(`fmt.Sprintf("%v", ${visit(span.expression)})`);
537
+ if (span.literal.text.length > 0) {
538
+ parts.push(toGoStringLiteral(span.literal.text));
539
+ }
540
+ }
541
+ if (parts.length === 0) {
542
+ return '""';
543
+ }
544
+ return parts.join(' + ');
545
+ }
546
+ function hasQuestionDot(node) {
547
+ return ('questionDotToken' in node &&
548
+ !!node.questionDotToken);
549
+ }
550
+ function getTempName(prefix) {
551
+ return `__${prefix}_${goSafeId()}__`;
552
+ }
553
+ function inferExpectedTypeFromContext(node) {
554
+ const parent = node.parent;
555
+ if (ts.isVariableDeclaration(parent) && parent.initializer === node && parent.type) {
556
+ return getType(parent.type);
557
+ }
558
+ if (ts.isReturnStatement(parent)) {
559
+ let scope = parent.parent;
560
+ while (scope) {
561
+ if (ts.isFunctionDeclaration(scope) ||
562
+ ts.isMethodDeclaration(scope) ||
563
+ ts.isFunctionExpression(scope) ||
564
+ ts.isArrowFunction(scope)) {
565
+ if (scope.type)
566
+ return getType(scope.type);
567
+ break;
568
+ }
569
+ scope = scope.parent;
570
+ }
571
+ }
572
+ return undefined;
573
+ }
574
+ function inferExpressionType(expr) {
575
+ if (ts.isParenthesizedExpression(expr))
576
+ return inferExpressionType(expr.expression);
577
+ if (ts.isNonNullExpression(expr))
578
+ return inferExpressionType(expr.expression);
579
+ if (ts.isAsExpression(expr))
580
+ return getType(expr.type);
581
+ if (ts.isTypeAssertionExpression(expr))
582
+ return getType(expr.type);
583
+ if (ts.isStringLiteral(expr) ||
584
+ ts.isNoSubstitutionTemplateLiteral(expr) ||
585
+ ts.isTemplateExpression(expr)) {
586
+ return 'string';
587
+ }
588
+ if (ts.isNumericLiteral(expr))
589
+ return 'float64';
590
+ if (expr.kind === ts.SyntaxKind.TrueKeyword || expr.kind === ts.SyntaxKind.FalseKeyword)
591
+ return 'bool';
592
+ if (expr.kind === ts.SyntaxKind.NullKeyword)
593
+ return 'nil';
594
+ if (ts.isIdentifier(expr))
595
+ return variableGoTypes.get(expr.text);
596
+ if (ts.isArrayLiteralExpression(expr)) {
597
+ if (expr.elements.length === 0)
598
+ return '[]interface{}';
599
+ const firstElementType = inferExpressionType(expr.elements[0]) ?? 'interface{}';
600
+ return `[]${firstElementType}`;
601
+ }
602
+ if (ts.isPropertyAccessExpression(expr)) {
603
+ if (ts.isIdentifier(expr.expression) && enumNames.has(expr.expression.text)) {
604
+ const enumType = getSafeName(expr.expression.text);
605
+ return enumType;
606
+ }
607
+ const leftType = inferExpressionType(expr.expression);
608
+ if (expr.name.text === 'length')
609
+ return 'float64';
610
+ const resolvedLeftType = leftType?.replace(/^\*/, '').replace(/\[.*\]$/, '');
611
+ const resolvedPropertyType = resolvedLeftType
612
+ ? (classPropertyTypes.get(resolvedLeftType)?.get(expr.name.text) ??
613
+ interfacePropertyTypes.get(resolvedLeftType)?.get(expr.name.text))
614
+ : undefined;
615
+ if (hasQuestionDot(expr)) {
616
+ if (leftType && leftType.startsWith('*')) {
617
+ const memberType = resolvedPropertyType ?? 'interface{}';
618
+ return makeNullableType(memberType);
619
+ }
620
+ return 'interface{}';
621
+ }
622
+ if (resolvedPropertyType) {
623
+ return resolvedPropertyType;
624
+ }
625
+ if (ts.isIdentifier(expr.expression)) {
626
+ const className = variableClassNames.get(expr.expression.text);
627
+ const memberType = className
628
+ ? classPropertyTypes.get(className)?.get(expr.name.text)
629
+ : undefined;
630
+ if (memberType)
631
+ return memberType;
632
+ }
633
+ }
634
+ if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) {
635
+ const methodName = expr.expression.name.text;
636
+ const ownerType = inferExpressionType(expr.expression.expression);
637
+ if (isArrayLikeGoType(ownerType)) {
638
+ const elementType = getArrayElementTypeFromGoType(ownerType);
639
+ if (methodName === 'map') {
640
+ const callback = expr.arguments[0];
641
+ const mappedType = callback
642
+ ? inferArrayCallbackReturnType(callback, elementType, elementType)
643
+ : elementType;
644
+ return `[]${mappedType}`;
645
+ }
646
+ if (methodName === 'filter')
647
+ return `[]${elementType}`;
648
+ if (methodName === 'some')
649
+ return 'bool';
650
+ if (methodName === 'find')
651
+ return elementType;
652
+ if (methodName === 'join')
653
+ return 'string';
654
+ }
655
+ if (ownerType && ownerType.startsWith('*')) {
656
+ const className = ownerType.replace(/^\*/, '').replace(/\[.*\]$/, '');
657
+ const returnType = classMethodReturnTypes.get(className)?.get(methodName);
658
+ if (returnType) {
659
+ if (hasQuestionDot(expr) || hasQuestionDot(expr.expression)) {
660
+ return makeNullableType(returnType);
661
+ }
662
+ return returnType;
663
+ }
664
+ }
665
+ if (ts.isIdentifier(expr.expression.expression)) {
666
+ const className = variableClassNames.get(expr.expression.expression.text);
667
+ const returnType = className
668
+ ? classMethodReturnTypes.get(className)?.get(methodName)
669
+ : undefined;
670
+ if (returnType) {
671
+ if (hasQuestionDot(expr) || hasQuestionDot(expr.expression)) {
672
+ return makeNullableType(returnType);
673
+ }
674
+ return returnType;
675
+ }
676
+ }
677
+ }
678
+ if (ts.isConditionalExpression(expr)) {
679
+ const whenTrueType = inferExpressionType(expr.whenTrue);
680
+ const whenFalseType = inferExpressionType(expr.whenFalse);
681
+ if (whenTrueType && whenTrueType === whenFalseType)
682
+ return whenTrueType;
683
+ return whenTrueType ?? whenFalseType;
684
+ }
685
+ if (ts.isBinaryExpression(expr) &&
686
+ expr.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
687
+ const leftType = inferExpressionType(expr.left);
688
+ const rightType = inferExpressionType(expr.right);
689
+ if (leftType && leftType.startsWith('*') && rightType === leftType.slice(1)) {
690
+ return rightType;
691
+ }
692
+ return rightType ?? leftType;
693
+ }
694
+ return undefined;
695
+ }
696
+ function makeNullableType(typeName) {
697
+ if (!typeName || typeName === 'interface{}' || typeName.startsWith('*'))
698
+ return typeName || 'interface{}';
699
+ if (['string', 'float64', 'bool'].includes(typeName))
700
+ return `*${typeName}`;
701
+ return typeName;
702
+ }
703
+ function visitConditionalExpression(node) {
704
+ const whenTrue = visit(node.whenTrue);
705
+ const whenFalse = visit(node.whenFalse);
706
+ const resultType = inferExpectedTypeFromContext(node) ||
707
+ (() => {
708
+ const whenTrueType = inferExpressionType(node.whenTrue);
709
+ const whenFalseType = inferExpressionType(node.whenFalse);
710
+ if (whenTrueType && whenTrueType === whenFalseType)
711
+ return whenTrueType;
712
+ return whenTrueType ?? whenFalseType ?? 'interface{}';
713
+ })();
714
+ return `func() ${resultType} { if ${visit(node.condition)} { return ${whenTrue} }; return ${whenFalse} }()`;
715
+ }
716
+ function visitNullishCoalescingExpression(node) {
717
+ const leftType = inferExpressionType(node.left);
718
+ const rightType = inferExpressionType(node.right);
719
+ if (leftType && leftType.startsWith('*')) {
720
+ const leftValueType = leftType.slice(1);
721
+ const expectedType = inferExpectedTypeFromContext(node);
722
+ const resultType = expectedType || (rightType === leftValueType ? leftValueType : (rightType ?? leftType));
723
+ const tmp = getTempName('nullish');
724
+ const leftExpr = visit(node.left);
725
+ const rightExpr = visit(node.right);
726
+ const returnLeft = resultType === leftValueType ? `*${tmp}` : tmp;
727
+ return `func() ${resultType} { ${tmp} := ${leftExpr}; if ${tmp} == nil { return ${rightExpr} }; return ${returnLeft} }()`;
728
+ }
729
+ return visit(node.left);
730
+ }
731
+ function visitOptionalPropertyAccess(node) {
732
+ const baseExpr = visit(node.expression);
733
+ const baseType = inferExpressionType(node.expression);
734
+ if (!baseType || !baseType.startsWith('*')) {
735
+ const objectType = resolveExpressionType(node.expression);
736
+ return getAcessString(baseExpr, visit(node.name), objectType);
737
+ }
738
+ const className = baseType.replace(/^\*/, '').replace(/\[.*\]$/, '');
739
+ const propertyType = classPropertyTypes.get(className)?.get(node.name.text) ?? 'interface{}';
740
+ const nullableType = makeNullableType(propertyType);
741
+ const tmp = getTempName('opt');
742
+ const propertyAccess = `${tmp}.${visit(node.name)}`;
743
+ if (nullableType.startsWith('*') && nullableType.slice(1) === propertyType) {
744
+ const valueTemp = getTempName('optv');
745
+ return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${propertyAccess}; return &${valueTemp} }()`;
746
+ }
747
+ return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; return ${propertyAccess} }()`;
748
+ }
749
+ function visitOptionalElementAccess(node) {
750
+ const baseExpr = visit(node.expression);
751
+ const baseType = inferExpressionType(node.expression);
752
+ if (!baseType || !baseType.startsWith('*')) {
753
+ return `${baseExpr}[int(${visit(node.argumentExpression)})]`;
754
+ }
755
+ const valueType = inferExpectedTypeFromContext(node) ?? 'interface{}';
756
+ const nullableType = makeNullableType(valueType);
757
+ const tmp = getTempName('opte');
758
+ const elementExpr = `${tmp}[int(${visit(node.argumentExpression)})]`;
759
+ if (nullableType.startsWith('*') && nullableType.slice(1) === valueType) {
760
+ const valueTemp = getTempName('optev');
761
+ return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${elementExpr}; return &${valueTemp} }()`;
762
+ }
763
+ return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; return ${elementExpr} }()`;
764
+ }
765
+ function visitOptionalCall(node) {
766
+ if (!ts.isPropertyAccessExpression(node.expression)) {
767
+ return `${visit(node.expression)}(${node.arguments.map((a) => visit(a)).join(', ')})`;
768
+ }
769
+ const baseNode = node.expression.expression;
770
+ const methodName = node.expression.name.text;
771
+ const baseExpr = visit(baseNode);
772
+ const baseType = inferExpressionType(baseNode);
773
+ const args = node.arguments.map((a) => visit(a)).join(', ');
774
+ if (!baseType || !baseType.startsWith('*')) {
775
+ return `${baseExpr}.${methodName}(${args})`;
776
+ }
777
+ const className = baseType.replace(/^\*/, '').replace(/\[.*\]$/, '');
778
+ const returnType = classMethodReturnTypes.get(className)?.get(methodName) ?? 'interface{}';
779
+ const nullableType = makeNullableType(returnType);
780
+ const tmp = getTempName('optc');
781
+ const callExpr = `${tmp}.${methodName}(${args})`;
782
+ if (nullableType.startsWith('*') && nullableType.slice(1) === returnType) {
783
+ const valueTemp = getTempName('optcv');
784
+ return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${callExpr}; return &${valueTemp} }()`;
785
+ }
786
+ return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; return ${callExpr} }()`;
787
+ }
788
+ function isArrayLikeGoType(goType) {
789
+ return !!goType && goType.startsWith('[]');
790
+ }
791
+ function getArrayElementTypeFromGoType(goType) {
792
+ if (!goType.startsWith('[]'))
793
+ return 'interface{}';
794
+ const elementType = goType.slice(2);
795
+ return elementType || 'interface{}';
796
+ }
797
+ function inferArrayCallbackReturnType(callback, elementType, fallbackType) {
798
+ if (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) {
799
+ if (callback.type) {
800
+ const explicitType = getType(callback.type);
801
+ return explicitType || fallbackType;
802
+ }
803
+ if (ts.isBlock(callback.body)) {
804
+ return fallbackType;
805
+ }
806
+ const inferred = inferExpressionType(callback.body);
807
+ return inferred ?? fallbackType;
808
+ }
809
+ if (ts.isIdentifier(callback)) {
810
+ const knownType = variableGoTypes.get(callback.text);
811
+ if (knownType)
812
+ return knownType;
813
+ }
814
+ return fallbackType;
815
+ }
816
+ function buildArrayCallbackInfo(callback, elementType, forcedReturnType) {
817
+ if (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) {
818
+ const paramCount = callback.parameters.length;
819
+ const callbackReturnType = forcedReturnType ?? inferArrayCallbackReturnType(callback, elementType, 'interface{}');
820
+ const params = [];
821
+ if (paramCount > 0) {
822
+ params.push(`${visit(callback.parameters[0].name)} ${elementType}`);
823
+ }
824
+ if (paramCount > 1) {
825
+ params.push(`${visit(callback.parameters[1].name)} float64`);
826
+ }
827
+ if (paramCount > 2) {
828
+ params.push(`${visit(callback.parameters[2].name)} []${elementType}`);
829
+ }
830
+ const body = ts.isBlock(callback.body)
831
+ ? visit(callback.body)
832
+ : `{\n\t\treturn ${visit(callback.body)};\n\t}`;
833
+ return {
834
+ fnExpr: `func(${params.join(', ')}) ${callbackReturnType} ${body}`,
835
+ paramCount,
836
+ returnType: callbackReturnType
837
+ };
838
+ }
839
+ return {
840
+ fnExpr: visit(callback),
841
+ paramCount: 1,
842
+ returnType: forcedReturnType ?? 'interface{}'
843
+ };
844
+ }
845
+ function buildArrayCallbackInvocation(callbackInfo, itemVar, indexVar, arrayVar) {
846
+ const args = [];
847
+ if (callbackInfo.paramCount > 0)
848
+ args.push(itemVar);
849
+ if (callbackInfo.paramCount > 1)
850
+ args.push(`float64(${indexVar})`);
851
+ if (callbackInfo.paramCount > 2)
852
+ args.push(arrayVar);
853
+ return `(${callbackInfo.fnExpr})(${args.join(', ')})`;
854
+ }
855
+ function visitArrayHigherOrderCall(node) {
856
+ if (!ts.isPropertyAccessExpression(node.expression))
857
+ return undefined;
858
+ const methodName = node.expression.name.text;
859
+ if (!['map', 'filter', 'some', 'find', 'join'].includes(methodName)) {
860
+ return undefined;
861
+ }
862
+ const arrayExprNode = node.expression.expression;
863
+ const arrayExpr = visit(arrayExprNode);
864
+ const ownerType = inferExpressionType(arrayExprNode);
865
+ const elementType = isArrayLikeGoType(ownerType)
866
+ ? getArrayElementTypeFromGoType(ownerType)
867
+ : 'interface{}';
868
+ if (methodName === 'join') {
869
+ importedPackages.add('strings');
870
+ importedPackages.add('fmt');
871
+ const separator = node.arguments[0] ? visit(node.arguments[0]) : '""';
872
+ const arrVar = getTempName('arrjoin');
873
+ const partsVar = getTempName('parts');
874
+ return `func() string { ${arrVar} := ${arrayExpr}; ${partsVar} := make([]string, len(${arrVar})); for i, v := range ${arrVar} { ${partsVar}[i] = fmt.Sprintf("%v", v) }; return strings.Join(${partsVar}, ${separator}) }()`;
875
+ }
876
+ const callback = node.arguments[0];
877
+ if (!callback) {
878
+ return undefined;
879
+ }
880
+ const arrVar = getTempName('arrhof');
881
+ const idxVar = getTempName('i');
882
+ const itemVar = getTempName('item');
883
+ if (methodName === 'map') {
884
+ const callbackInfo = buildArrayCallbackInfo(callback, elementType, elementType);
885
+ const mappedType = callbackInfo.returnType || 'interface{}';
886
+ const resultVar = getTempName('mapres');
887
+ const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
888
+ const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
889
+ return `func() []${mappedType} { ${arrVar} := ${arrayExpr}; ${resultVar} := make([]${mappedType}, 0, len(${arrVar})); for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { ${resultVar} = append(${resultVar}, ${callbackCall}) }; return ${resultVar} }()`;
890
+ }
891
+ if (methodName === 'filter') {
892
+ const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
893
+ const resultVar = getTempName('filterres');
894
+ const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
895
+ const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
896
+ return `func() []${elementType} { ${arrVar} := ${arrayExpr}; ${resultVar} := make([]${elementType}, 0, len(${arrVar})); for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { ${resultVar} = append(${resultVar}, ${itemVar}) } }; return ${resultVar} }()`;
897
+ }
898
+ if (methodName === 'some') {
899
+ const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
900
+ const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
901
+ const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
902
+ return `func() bool { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { return true } }; return false }()`;
903
+ }
904
+ const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
905
+ const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
906
+ const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
907
+ return `func() ${elementType} { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { return ${itemVar} } }; var __zero ${elementType}; return __zero }()`;
908
+ }
909
+ function getAliasType(name, seen = new Set()) {
910
+ const aliasType = typeAliases.get(name);
911
+ if (!aliasType)
912
+ return undefined;
913
+ if (seen.has(name))
914
+ return undefined;
915
+ if (ts.isTypeReferenceNode(aliasType) && ts.isIdentifier(aliasType.typeName)) {
916
+ const nestedName = aliasType.typeName.text;
917
+ if (typeAliases.has(nestedName)) {
918
+ seen.add(name);
919
+ return getAliasType(nestedName, seen) ?? aliasType;
920
+ }
921
+ }
922
+ return aliasType;
923
+ }
924
+ function getOptionalNodeType(typeNode, isOptional) {
925
+ const baseType = typeNode ? getType(typeNode) : 'interface{}';
926
+ if (!isOptional)
927
+ return baseType;
928
+ if (baseType === 'interface{}' || baseType.startsWith('*'))
929
+ return baseType;
930
+ if (['string', 'float64', 'bool'].includes(baseType))
931
+ return `*${baseType}`;
932
+ return baseType;
933
+ }
934
+ function getEnumMemberName(name) {
935
+ if (ts.isIdentifier(name)) {
936
+ return getSafeName(name.text);
937
+ }
938
+ if (ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
939
+ const sanitized = name.text.replace(/[^a-zA-Z0-9_]/g, '_');
940
+ return sanitized.length > 0 ? sanitized : 'Member';
941
+ }
942
+ return 'Member';
943
+ }
944
+ function getEnumBaseType(node) {
945
+ for (const member of node.members) {
946
+ const initializer = member.initializer;
947
+ if (!initializer)
948
+ continue;
949
+ if (ts.isStringLiteral(initializer) || ts.isNoSubstitutionTemplateLiteral(initializer)) {
950
+ return 'string';
951
+ }
952
+ }
953
+ return 'float64';
954
+ }
955
+ function readNumericEnumInitializer(initializer) {
956
+ if (ts.isNumericLiteral(initializer)) {
957
+ return Number(initializer.text);
958
+ }
959
+ if (ts.isPrefixUnaryExpression(initializer) &&
960
+ initializer.operator === ts.SyntaxKind.MinusToken &&
961
+ ts.isNumericLiteral(initializer.operand)) {
962
+ return -Number(initializer.operand.text);
963
+ }
964
+ return undefined;
965
+ }
966
+ function visitEnumDeclaration(node) {
967
+ const enumName = getSafeName(node.name.text);
968
+ const baseType = enumBaseTypes.get(node.name.text) ?? getEnumBaseType(node);
969
+ let nextNumericValue = 0;
970
+ let canAutoIncrement = true;
971
+ const members = [];
972
+ for (const member of node.members) {
973
+ const memberName = getEnumMemberName(member.name);
974
+ const symbolName = `${enumName}_${memberName}`;
975
+ let valueExpr;
976
+ if (member.initializer) {
977
+ if (baseType === 'float64') {
978
+ const numericValue = readNumericEnumInitializer(member.initializer);
979
+ if (numericValue !== undefined) {
980
+ valueExpr = `${numericValue}`;
981
+ nextNumericValue = numericValue + 1;
982
+ canAutoIncrement = true;
983
+ }
984
+ else {
985
+ valueExpr = `float64(${visit(member.initializer)})`;
986
+ canAutoIncrement = false;
987
+ }
988
+ }
989
+ else {
990
+ valueExpr = visit(member.initializer);
991
+ }
992
+ }
993
+ else if (baseType === 'float64') {
994
+ const currentValue = canAutoIncrement ? nextNumericValue : 0;
995
+ valueExpr = `${currentValue}`;
996
+ nextNumericValue = currentValue + 1;
997
+ }
998
+ else {
999
+ valueExpr = toGoStringLiteral(memberName);
1000
+ }
1001
+ members.push(`\t${symbolName} ${enumName} = ${enumName}(${valueExpr})`);
1002
+ }
1003
+ return `type ${enumName} ${baseType}\n\nvar (\n${members.join('\n')}\n)`;
1004
+ }
411
1005
  function getType(typeNode, getArrayType = false) {
412
1006
  if (!typeNode)
413
1007
  return ':';
1008
+ if (ts.isArrayTypeNode(typeNode)) {
1009
+ const elementType = getType(typeNode.elementType);
1010
+ return getArrayType ? elementType : `[]${elementType}`;
1011
+ }
414
1012
  // Handle union types (e.g. string | null, number | undefined)
415
1013
  if (ts.isUnionTypeNode(typeNode)) {
416
1014
  const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
@@ -430,6 +1028,13 @@ function getType(typeNode, getArrayType = false) {
430
1028
  }
431
1029
  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
432
1030
  const name = typeNode.typeName.text;
1031
+ if (enumNames.has(name)) {
1032
+ return getSafeName(name);
1033
+ }
1034
+ const aliasType = getAliasType(name);
1035
+ if (aliasType) {
1036
+ return getType(aliasType, getArrayType);
1037
+ }
433
1038
  if (name === 'Promise' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
434
1039
  return `chan ${getType(typeNode.typeArguments[0])}`;
435
1040
  }
@@ -492,6 +1097,20 @@ function getTypeCategory(typeNode) {
492
1097
  }
493
1098
  return undefined;
494
1099
  }
1100
+ function getClassNameFromTypeNode(typeNode) {
1101
+ if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
1102
+ return classNames.has(typeNode.typeName.text) ? typeNode.typeName.text : undefined;
1103
+ }
1104
+ if (ts.isUnionTypeNode(typeNode)) {
1105
+ const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
1106
+ t.kind !== ts.SyntaxKind.UndefinedKeyword &&
1107
+ !(ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword));
1108
+ if (nonNullTypes.length === 1) {
1109
+ return getClassNameFromTypeNode(nonNullTypes[0]);
1110
+ }
1111
+ }
1112
+ return undefined;
1113
+ }
495
1114
  function resolveExpressionType(expr) {
496
1115
  if (ts.isIdentifier(expr)) {
497
1116
  return variableTypes.get(expr.text);
@@ -772,6 +1391,59 @@ function getTypeArguments(typeArguments) {
772
1391
  const args = typeArguments.map((ta) => getType(ta));
773
1392
  return `[${args.join(', ')}]`;
774
1393
  }
1394
+ function getParameterGoType(param) {
1395
+ if (param.type) {
1396
+ const explicitType = getType(param.type);
1397
+ return explicitType === ':' ? 'interface{}' : explicitType;
1398
+ }
1399
+ if (param.initializer) {
1400
+ const inferredType = inferExpressionType(param.initializer);
1401
+ if (inferredType && inferredType !== 'nil' && inferredType !== ':') {
1402
+ return inferredType;
1403
+ }
1404
+ }
1405
+ return 'interface{}';
1406
+ }
1407
+ function getFunctionParametersInfo(parameters) {
1408
+ if (parameters.length === 0) {
1409
+ return { signature: '', prefixBlockContent: '' };
1410
+ }
1411
+ const firstDefaultIndex = parameters.findIndex((p) => !!p.initializer);
1412
+ if (firstDefaultIndex === -1) {
1413
+ return {
1414
+ signature: parameters.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`).join(', '),
1415
+ prefixBlockContent: ''
1416
+ };
1417
+ }
1418
+ const hasRequiredAfterDefault = parameters
1419
+ .slice(firstDefaultIndex)
1420
+ .some((p) => !p.initializer);
1421
+ if (hasRequiredAfterDefault) {
1422
+ return {
1423
+ signature: parameters.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`).join(', '),
1424
+ prefixBlockContent: ''
1425
+ };
1426
+ }
1427
+ const requiredParams = parameters.slice(0, firstDefaultIndex);
1428
+ const defaultedParams = parameters.slice(firstDefaultIndex);
1429
+ const signatureParts = requiredParams.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`);
1430
+ signatureParts.push('__defaultArgs ...interface{}');
1431
+ const prefixBlockContent = defaultedParams
1432
+ .map((param, index) => {
1433
+ const paramName = visit(param.name);
1434
+ const paramType = getParameterGoType(param);
1435
+ const defaultValue = visit(param.initializer);
1436
+ if (paramType === 'interface{}') {
1437
+ return `var ${paramName} interface{}\n\t\tif len(__defaultArgs) > ${index} {\n\t\t\t${paramName} = __defaultArgs[${index}]\n\t\t} else {\n\t\t\t${paramName} = ${defaultValue}\n\t\t}\n\t\t`;
1438
+ }
1439
+ return `var ${paramName} ${paramType}\n\t\tif len(__defaultArgs) > ${index} {\n\t\t\t${paramName} = __defaultArgs[${index}].(${paramType})\n\t\t} else {\n\t\t\t${paramName} = ${defaultValue}\n\t\t}\n\t\t`;
1440
+ })
1441
+ .join('');
1442
+ return {
1443
+ signature: signatureParts.join(', '),
1444
+ prefixBlockContent
1445
+ };
1446
+ }
775
1447
  function getSafeName(name) {
776
1448
  if (!dangerousNames.has(name)) {
777
1449
  return name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typenative",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "Build native applications using Typescript.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,13 @@
25
25
  "test11": "node ./bin/index --source test/test11.spec.ts --script",
26
26
  "test12": "node ./bin/index --source test/test12.spec.ts --script",
27
27
  "test13": "node ./bin/index --source test/test13.spec.ts --script",
28
- "test14": "node ./bin/index --source test/test14.spec.ts --script"
28
+ "test14": "node ./bin/index --source test/test14.spec.ts --script",
29
+ "test15": "node ./bin/index --source test/test15.spec.ts --script",
30
+ "test16": "node ./bin/index --source test/test16.spec.ts --script",
31
+ "test17": "node ./bin/index --source test/test17.spec.ts --script",
32
+ "test18": "node ./bin/index --source test/test18.spec.ts --script",
33
+ "test19": "node ./bin/index --source test/test19.spec.ts --script",
34
+ "test20": "node ./bin/index --source test/Test20.spec.ts --script"
29
35
  },
30
36
  "repository": {
31
37
  "type": "git",
@@ -81,6 +81,22 @@ interface Array<T> extends IterableIterator<T> {
81
81
  * Returns a copy of a section of an array.
82
82
  */
83
83
  slice(start?: number, end?: number): T[];
84
+ /**
85
+ * Calls a defined callback function on each element of an array, and returns an array that contains the results.
86
+ */
87
+ map<U>(callback: (value: T, index?: number, array?: T[]) => U): U[];
88
+ /**
89
+ * Returns the elements of an array that meet the condition specified in a callback function.
90
+ */
91
+ filter(callback: (value: T, index?: number, array?: T[]) => boolean): T[];
92
+ /**
93
+ * Determines whether the specified callback function returns true for any element of an array.
94
+ */
95
+ some(callback: (value: T, index?: number, array?: T[]) => boolean): boolean;
96
+ /**
97
+ * Returns the value of the first element in the array where callback is true, and undefined otherwise.
98
+ */
99
+ find(callback: (value: T, index?: number, array?: T[]) => boolean): T | undefined;
84
100
  /**
85
101
  * Returns the index of the first occurrence of a value in an array, or -1 if it is not present.
86
102
  */