typenative 0.0.15 → 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 +51 -0
- package/README.md +135 -48
- package/bin/index.js +1 -1
- package/bin/transpiler.js +754 -22
- package/package.json +8 -2
- package/types/typenative.d.ts +25 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,28 +5,77 @@ 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
|
+
|
|
23
|
+
## [0.0.16] - 2025-02-15
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- RegExp support: regex literals (`/pattern/flags`), `new RegExp()`, `test()`, and `exec()` mapped to Go's `regexp` package
|
|
28
|
+
- Universal `toString()` support for numbers, booleans, arrays, and objects
|
|
29
|
+
|
|
30
|
+
## [0.0.15] - 2025-02-15
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Classes with constructors, inheritance (`extends`, `super()`), and methods transpiled to Go structs
|
|
35
|
+
- Interfaces with method signatures and `extends` transpiled to Go interfaces
|
|
36
|
+
- Generics support for functions and classes via Go type parameters
|
|
37
|
+
- Async/Await transpiled to Go channels and goroutines
|
|
38
|
+
- Promises (`new Promise`) transpiled to channel + goroutine pattern
|
|
39
|
+
- `setTimeout` mapped to Go's `time.AfterFunc`
|
|
40
|
+
- Nullable types (`T | null`, `T | undefined`) transpiled to Go pointer types
|
|
41
|
+
- Object literals transpiled to Go struct literals
|
|
42
|
+
- `assert()` function transpiled to `panic` on failure
|
|
43
|
+
- `parseInt` and `parseFloat` mapped to Go's `strconv` package
|
|
44
|
+
- String methods: `split`, `trim`, `trimStart`, `trimEnd`, `toUpperCase`, `toLowerCase`, `indexOf`, `includes`, `startsWith`, `endsWith`, `replace`, `replaceAll`, `repeat`, `charAt`, `substring`, `slice`, `concat`, `toString`
|
|
45
|
+
- Array methods: `join`, `slice`, `toString` (in addition to existing `push`)
|
|
46
|
+
- Math methods: `ceil`, `round`, `abs`, `sqrt`, `pow`, `min`, `max` (in addition to existing `random`, `floor`)
|
|
47
|
+
- Non-null assertion operator (`!`) support
|
|
48
|
+
- Type-aware method dispatch to prevent class methods from being intercepted as built-in string/array methods
|
|
49
|
+
- Safe name collision avoidance for Go reserved identifiers
|
|
50
|
+
- Automatic Go import management for `fmt`, `math`, `math/rand`, `strings`, `strconv`, `time`
|
|
51
|
+
|
|
8
52
|
## [0.0.14] - 2025-05-25
|
|
9
53
|
|
|
10
54
|
### Added
|
|
55
|
+
|
|
11
56
|
- Project creation with new `--new` command
|
|
12
57
|
- Support for functions and arrow functions
|
|
13
58
|
- Support for switch statements
|
|
14
59
|
- If statements, while loops, and for...of loops for arrays
|
|
15
60
|
|
|
16
61
|
### Changed
|
|
62
|
+
|
|
17
63
|
- Replaced shelljs dependency with execa for improved process execution
|
|
18
64
|
|
|
19
65
|
### Fixed
|
|
66
|
+
|
|
20
67
|
- Project creation issues
|
|
21
68
|
|
|
22
69
|
## [0.0.12] - 2025-05-23
|
|
23
70
|
|
|
24
71
|
### Changed
|
|
72
|
+
|
|
25
73
|
- Switched transpilation target to Go language
|
|
26
74
|
|
|
27
75
|
## [0.0.9] - 2024-03-01
|
|
28
76
|
|
|
29
77
|
### Added
|
|
78
|
+
|
|
30
79
|
- Basic types support (number, boolean, string, null, any)
|
|
31
80
|
- Support for variable declarations
|
|
32
81
|
- Support for binary expressions
|
|
@@ -35,10 +84,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
35
84
|
## [0.0.1] - 2024-02-24
|
|
36
85
|
|
|
37
86
|
### Added
|
|
87
|
+
|
|
38
88
|
- Initial project setup
|
|
39
89
|
- Basic project structure
|
|
40
90
|
|
|
41
91
|
## [0.0.0] - 2022-12-20
|
|
42
92
|
|
|
43
93
|
### Added
|
|
94
|
+
|
|
44
95
|
- Initial commit
|
package/README.md
CHANGED
|
@@ -11,55 +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
|
-
##
|
|
15
|
+
## Typescript Syntax Support
|
|
17
16
|
|
|
18
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
| **Variables** | | |
|
|
35
|
-
| Variable declarations | ✅ | `let` and `const` |
|
|
36
|
-
| **Operators** | | |
|
|
37
|
-
| Arithmetic operators | ✅ | `+`, `-`, etc. |
|
|
38
|
-
| Comparison operators | ✅ | `==`, `!=`, etc. |
|
|
39
|
-
| Logical operators | ✅ | `&&`, `\|\|` |
|
|
40
|
-
| Increment/Decrement | ✅ | `++`, `--` |
|
|
41
|
-
| **Control Flow** | | |
|
|
42
|
-
| For loops | ✅ | Standard `for` loops |
|
|
43
|
-
| For...of loops | ✅ | Iteration over arrays |
|
|
44
|
-
| While loops | ✅ | Transpiled to Go's `for` loops |
|
|
45
|
-
| Do...while loops | ✅ | Implemented with conditional break |
|
|
46
|
-
| If/Else statements | ✅ | Fully supported |
|
|
47
|
-
| Switch statements | ✅ | Case and default statements |
|
|
48
|
-
| **Data Structures** | | |
|
|
49
|
-
| Arrays | ✅ | Basic array operations |
|
|
50
|
-
| Array methods | ✅ | `push` supported |
|
|
51
|
-
| **Functions** | | |
|
|
52
|
-
| Function declarations | ✅ | Transpiled to Go functions |
|
|
53
|
-
| Arrow Functions | ✅ | Transpiled to anonymous functions |
|
|
54
|
-
| console.log | ✅ | Mapped to `fmt.Println` |
|
|
55
|
-
| console.time/timeEnd | ✅ | Performance measurement |
|
|
56
|
-
| Math.random | ✅ | Mapped to Go's `rand.Float64()` |
|
|
57
|
-
| Math.floor | ✅ | Mapped to Go's `math.Floor()` |
|
|
58
|
-
| **Unsupported Features** | | |
|
|
59
|
-
| Classes | ❌ | Not implemented |
|
|
60
|
-
| Interfaces | ❌ | Not implemented |
|
|
61
|
-
| Async/Await | ❌ | Not implemented |
|
|
62
|
-
| Modules/Imports | ❌ | Not implemented |
|
|
63
|
-
| Generics | ❌ | Not implemented |
|
|
64
|
-
|
|
65
|
-
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/index.js
CHANGED
|
@@ -105,7 +105,7 @@ function getPackageJson(projectName) {
|
|
|
105
105
|
build: `npx typenative --source main.ts --output bin/${projectName}.exe`
|
|
106
106
|
},
|
|
107
107
|
devDependencies: {
|
|
108
|
-
typenative: '^0.0.
|
|
108
|
+
typenative: '^0.0.16'
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
111
|
return JSON.stringify(pckg, null, 2);
|
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
|
|
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})`;
|
|
@@ -58,17 +83,38 @@ export function visit(node, options = {}) {
|
|
|
58
83
|
else if (ts.isToken(node) && node.kind === ts.SyntaxKind.NullKeyword) {
|
|
59
84
|
return `nil`;
|
|
60
85
|
}
|
|
86
|
+
else if (ts.isRegularExpressionLiteral(node)) {
|
|
87
|
+
importedPackages.add('regexp');
|
|
88
|
+
const text = node.text; // e.g. /pattern/flags
|
|
89
|
+
const lastSlash = text.lastIndexOf('/');
|
|
90
|
+
const pattern = text.substring(1, lastSlash);
|
|
91
|
+
const flags = text.substring(lastSlash + 1);
|
|
92
|
+
const goFlags = jsRegexFlagsToGo(flags);
|
|
93
|
+
const escaped = pattern.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
94
|
+
return `regexp.MustCompile("${goFlags}${escaped}")`;
|
|
95
|
+
}
|
|
61
96
|
else if (ts.isArrayLiteralExpression(node)) {
|
|
62
97
|
const type = ts.isVariableDeclaration(node.parent) ? getType(node.parent.type, true) : '';
|
|
63
98
|
return `[]${type} {${node.elements.map((e) => visit(e)).join(', ')}}`;
|
|
64
99
|
}
|
|
65
100
|
else if (ts.isBlock(node)) {
|
|
66
|
-
return `{\n\t\t${
|
|
101
|
+
return `{\n\t\t${options.prefixBlockContent ?? ''}${node.statements
|
|
102
|
+
.map((n) => visit(n))
|
|
103
|
+
.join('\t')}${options.extraBlockContent ?? ''}}${options.inline ? '' : '\n\t'}`;
|
|
67
104
|
}
|
|
68
105
|
else if (ts.isElementAccessExpression(node)) {
|
|
106
|
+
if (hasQuestionDot(node)) {
|
|
107
|
+
return visitOptionalElementAccess(node);
|
|
108
|
+
}
|
|
69
109
|
return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
|
|
70
110
|
}
|
|
71
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
|
+
}
|
|
72
118
|
const leftSide = visit(node.expression);
|
|
73
119
|
const rightSide = visit(node.name);
|
|
74
120
|
const objectType = resolveExpressionType(node.expression);
|
|
@@ -78,15 +124,37 @@ export function visit(node, options = {}) {
|
|
|
78
124
|
const type = getType(node.type);
|
|
79
125
|
// Track variable type for type-aware method dispatch
|
|
80
126
|
if (ts.isIdentifier(node.name)) {
|
|
127
|
+
if (node.type) {
|
|
128
|
+
variableGoTypes.set(node.name.text, getType(node.type));
|
|
129
|
+
}
|
|
81
130
|
const cat = node.type ? getTypeCategory(node.type) : undefined;
|
|
82
131
|
if (cat) {
|
|
83
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
|
+
}
|
|
84
139
|
}
|
|
85
140
|
else if (node.initializer &&
|
|
86
141
|
ts.isNewExpression(node.initializer) &&
|
|
87
142
|
ts.isIdentifier(node.initializer.expression)) {
|
|
88
|
-
if (
|
|
143
|
+
if (node.initializer.expression.text === 'RegExp') {
|
|
144
|
+
variableTypes.set(node.name.text, 'RegExp');
|
|
145
|
+
}
|
|
146
|
+
else if (classNames.has(node.initializer.expression.text)) {
|
|
89
147
|
variableTypes.set(node.name.text, 'class');
|
|
148
|
+
variableClassNames.set(node.name.text, node.initializer.expression.text);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (node.initializer && ts.isRegularExpressionLiteral(node.initializer)) {
|
|
152
|
+
variableTypes.set(node.name.text, 'RegExp');
|
|
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);
|
|
90
158
|
}
|
|
91
159
|
}
|
|
92
160
|
}
|
|
@@ -103,6 +171,10 @@ export function visit(node, options = {}) {
|
|
|
103
171
|
return `${type === ':' ? '' : 'var '}${visit(node.name)} ${type}${type === ':' ? '' : ' '}${initializer}`;
|
|
104
172
|
}
|
|
105
173
|
else if (ts.isCallExpression(node)) {
|
|
174
|
+
if (hasQuestionDot(node) ||
|
|
175
|
+
(ts.isPropertyAccessExpression(node.expression) && hasQuestionDot(node.expression))) {
|
|
176
|
+
return visitOptionalCall(node);
|
|
177
|
+
}
|
|
106
178
|
// Handle setTimeout specially to get raw delay value
|
|
107
179
|
if (ts.isIdentifier(node.expression) && node.expression.text === 'setTimeout') {
|
|
108
180
|
importedPackages.add('time');
|
|
@@ -111,6 +183,10 @@ export function visit(node, options = {}) {
|
|
|
111
183
|
const delay = ts.isNumericLiteral(delayNode) ? delayNode.text : visit(delayNode);
|
|
112
184
|
return `time.AfterFunc(${delay} * time.Millisecond, ${callback.trimEnd()})`;
|
|
113
185
|
}
|
|
186
|
+
const arrayHigherOrderCall = visitArrayHigherOrderCall(node);
|
|
187
|
+
if (arrayHigherOrderCall) {
|
|
188
|
+
return arrayHigherOrderCall;
|
|
189
|
+
}
|
|
114
190
|
const caller = visit(node.expression);
|
|
115
191
|
const safeCaller = getSafeName(caller);
|
|
116
192
|
const typeArgs = getTypeArguments(node.typeArguments);
|
|
@@ -128,7 +204,13 @@ export function visit(node, options = {}) {
|
|
|
128
204
|
else if (ts.isPostfixUnaryExpression(node)) {
|
|
129
205
|
return `${visit(node.operand, { inline: true })}${getOperatorText(node.operator)}`;
|
|
130
206
|
}
|
|
207
|
+
else if (ts.isConditionalExpression(node)) {
|
|
208
|
+
return visitConditionalExpression(node);
|
|
209
|
+
}
|
|
131
210
|
else if (ts.isBinaryExpression(node)) {
|
|
211
|
+
if (node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
212
|
+
return visitNullishCoalescingExpression(node);
|
|
213
|
+
}
|
|
132
214
|
let op = node.operatorToken.getText();
|
|
133
215
|
if (op === '===')
|
|
134
216
|
op = '==';
|
|
@@ -217,28 +299,56 @@ export function visit(node, options = {}) {
|
|
|
217
299
|
const name = visit(node.name, { inline: true });
|
|
218
300
|
const safeName = getSafeName(name);
|
|
219
301
|
const typeParams = getTypeParameters(node.typeParameters);
|
|
220
|
-
const
|
|
221
|
-
.map((p) => `${visit(p.name)} ${getType(p.type)}`)
|
|
222
|
-
.join(', ');
|
|
302
|
+
const parameterInfo = getFunctionParametersInfo(node.parameters);
|
|
223
303
|
const returnType = node.type ? ` ${getType(node.type)}` : '';
|
|
224
304
|
if (options.isOutside) {
|
|
225
|
-
return `func ${safeName}${typeParams}(${
|
|
305
|
+
return `func ${safeName}${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
|
|
306
|
+
prefixBlockContent: parameterInfo.prefixBlockContent
|
|
307
|
+
})}`;
|
|
226
308
|
}
|
|
227
|
-
return `${safeName} := func${typeParams}(${
|
|
309
|
+
return `${safeName} := func${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
|
|
310
|
+
prefixBlockContent: parameterInfo.prefixBlockContent
|
|
311
|
+
})}`;
|
|
228
312
|
}
|
|
229
313
|
else if (ts.isArrowFunction(node)) {
|
|
230
|
-
const
|
|
231
|
-
.map((p) => `${visit(p.name)} ${getType(p.type)}`)
|
|
232
|
-
.join(', ');
|
|
314
|
+
const parameterInfo = getFunctionParametersInfo(node.parameters);
|
|
233
315
|
const returnType = node.type ? ` ${getType(node.type)}` : '';
|
|
234
|
-
|
|
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
|
+
})}`;
|
|
235
322
|
}
|
|
236
323
|
else if (node.kind === ts.SyntaxKind.ThisKeyword) {
|
|
237
324
|
return 'self';
|
|
238
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
|
+
}
|
|
239
340
|
else if (ts.isInterfaceDeclaration(node)) {
|
|
240
341
|
if (options.addFunctionOutside) {
|
|
241
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
|
+
}
|
|
242
352
|
return '';
|
|
243
353
|
}
|
|
244
354
|
const name = visit(node.name);
|
|
@@ -254,6 +364,7 @@ export function visit(node, options = {}) {
|
|
|
254
364
|
}
|
|
255
365
|
}
|
|
256
366
|
const methods = [];
|
|
367
|
+
const properties = [];
|
|
257
368
|
for (const member of node.members) {
|
|
258
369
|
if (ts.isMethodSignature(member)) {
|
|
259
370
|
const methodName = visit(member.name);
|
|
@@ -263,6 +374,13 @@ export function visit(node, options = {}) {
|
|
|
263
374
|
const returnType = member.type ? ` ${getType(member.type)}` : '';
|
|
264
375
|
methods.push(`\t${methodName}(${params})${returnType}`);
|
|
265
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}`;
|
|
266
384
|
}
|
|
267
385
|
const members = [...extendedInterfaces.map((e) => `\t${e}`), ...methods];
|
|
268
386
|
return `type ${name}${typeParams} interface {\n${members.join('\n')}\n}`;
|
|
@@ -270,7 +388,20 @@ export function visit(node, options = {}) {
|
|
|
270
388
|
else if (ts.isClassDeclaration(node)) {
|
|
271
389
|
if (options.addFunctionOutside) {
|
|
272
390
|
outsideNodes.push(node);
|
|
273
|
-
|
|
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);
|
|
274
405
|
return '';
|
|
275
406
|
}
|
|
276
407
|
const name = visit(node.name);
|
|
@@ -296,7 +427,7 @@ export function visit(node, options = {}) {
|
|
|
296
427
|
fieldType = `[]${getType(member.type, true)}`;
|
|
297
428
|
}
|
|
298
429
|
else {
|
|
299
|
-
fieldType = member.type
|
|
430
|
+
fieldType = getOptionalNodeType(member.type, !!member.questionToken);
|
|
300
431
|
}
|
|
301
432
|
fields.push(`\t${fieldName} ${fieldType}`);
|
|
302
433
|
}
|
|
@@ -304,7 +435,7 @@ export function visit(node, options = {}) {
|
|
|
304
435
|
let result = `type ${name}${typeParams} struct {\n${fields.join('\n')}\n}\n\n`;
|
|
305
436
|
const ctor = node.members.find((m) => ts.isConstructorDeclaration(m));
|
|
306
437
|
if (ctor) {
|
|
307
|
-
const
|
|
438
|
+
const ctorParameterInfo = getFunctionParametersInfo(ctor.parameters);
|
|
308
439
|
const bodyStatements = ctor.body?.statements
|
|
309
440
|
.filter((s) => {
|
|
310
441
|
if (ts.isExpressionStatement(s) && ts.isCallExpression(s.expression)) {
|
|
@@ -314,7 +445,7 @@ export function visit(node, options = {}) {
|
|
|
314
445
|
})
|
|
315
446
|
.map((s) => visit(s))
|
|
316
447
|
.join('\t') ?? '';
|
|
317
|
-
result += `func New${name}${typeParams}(${
|
|
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`;
|
|
318
449
|
}
|
|
319
450
|
else {
|
|
320
451
|
result += `func New${name}${typeParams}() *${name}${typeParamNames} {\n\t\treturn &${name}${typeParamNames}{}\n\t}\n\n`;
|
|
@@ -322,11 +453,11 @@ export function visit(node, options = {}) {
|
|
|
322
453
|
for (const member of node.members) {
|
|
323
454
|
if (ts.isMethodDeclaration(member)) {
|
|
324
455
|
const methodName = visit(member.name);
|
|
325
|
-
const
|
|
326
|
-
.map((p) => `${visit(p.name)} ${getType(p.type)}`)
|
|
327
|
-
.join(', ');
|
|
456
|
+
const methodParameterInfo = getFunctionParametersInfo(member.parameters);
|
|
328
457
|
const returnType = member.type ? ` ${getType(member.type)}` : '';
|
|
329
|
-
result += `func (self *${name}${typeParamNames}) ${methodName}(${
|
|
458
|
+
result += `func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, {
|
|
459
|
+
prefixBlockContent: methodParameterInfo.prefixBlockContent
|
|
460
|
+
})}\n\n`;
|
|
330
461
|
}
|
|
331
462
|
}
|
|
332
463
|
return result.trim();
|
|
@@ -336,6 +467,19 @@ export function visit(node, options = {}) {
|
|
|
336
467
|
if (className === 'Promise') {
|
|
337
468
|
return visitNewPromise(node);
|
|
338
469
|
}
|
|
470
|
+
if (className === 'RegExp') {
|
|
471
|
+
importedPackages.add('regexp');
|
|
472
|
+
const nodeArgs = node.arguments ?? [];
|
|
473
|
+
if (nodeArgs.length >= 2 && ts.isStringLiteral(nodeArgs[1])) {
|
|
474
|
+
const pattern = visit(nodeArgs[0]);
|
|
475
|
+
const flags = jsRegexFlagsToGo(nodeArgs[1].text);
|
|
476
|
+
return `regexp.MustCompile("${flags}" + ${pattern})`;
|
|
477
|
+
}
|
|
478
|
+
if (nodeArgs.length >= 1) {
|
|
479
|
+
return `regexp.MustCompile(${visit(nodeArgs[0])})`;
|
|
480
|
+
}
|
|
481
|
+
return `regexp.MustCompile("")`;
|
|
482
|
+
}
|
|
339
483
|
const typeArgs = getTypeArguments(node.typeArguments);
|
|
340
484
|
const args = node.arguments ? node.arguments.map((a) => visit(a)) : [];
|
|
341
485
|
return `New${className}${typeArgs}(${args.join(', ')})`;
|
|
@@ -379,9 +523,492 @@ function getTypeText(typeNode) {
|
|
|
379
523
|
}
|
|
380
524
|
return getType(typeNode);
|
|
381
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
|
+
}
|
|
382
1005
|
function getType(typeNode, getArrayType = false) {
|
|
383
1006
|
if (!typeNode)
|
|
384
1007
|
return ':';
|
|
1008
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
1009
|
+
const elementType = getType(typeNode.elementType);
|
|
1010
|
+
return getArrayType ? elementType : `[]${elementType}`;
|
|
1011
|
+
}
|
|
385
1012
|
// Handle union types (e.g. string | null, number | undefined)
|
|
386
1013
|
if (ts.isUnionTypeNode(typeNode)) {
|
|
387
1014
|
const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
|
|
@@ -401,9 +1028,19 @@ function getType(typeNode, getArrayType = false) {
|
|
|
401
1028
|
}
|
|
402
1029
|
if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
|
|
403
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
|
+
}
|
|
404
1038
|
if (name === 'Promise' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
|
|
405
1039
|
return `chan ${getType(typeNode.typeArguments[0])}`;
|
|
406
1040
|
}
|
|
1041
|
+
if (name === 'RegExp') {
|
|
1042
|
+
return '*regexp.Regexp';
|
|
1043
|
+
}
|
|
407
1044
|
const typeArgs = getTypeArguments(typeNode.typeArguments);
|
|
408
1045
|
if (classNames.has(name)) {
|
|
409
1046
|
return `*${name}${typeArgs}`;
|
|
@@ -460,6 +1097,20 @@ function getTypeCategory(typeNode) {
|
|
|
460
1097
|
}
|
|
461
1098
|
return undefined;
|
|
462
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
|
+
}
|
|
463
1114
|
function resolveExpressionType(expr) {
|
|
464
1115
|
if (ts.isIdentifier(expr)) {
|
|
465
1116
|
return variableTypes.get(expr.text);
|
|
@@ -615,6 +1266,10 @@ const stringMethodHandlers = {
|
|
|
615
1266
|
return `fmt.Sprintf("%v", ${obj})`;
|
|
616
1267
|
}
|
|
617
1268
|
};
|
|
1269
|
+
const regexpMethodHandlers = {
|
|
1270
|
+
test: (obj, args) => `${obj}.MatchString(${args[0]})`,
|
|
1271
|
+
exec: (obj, args) => `${obj}.FindStringSubmatch(${args[0]})`
|
|
1272
|
+
};
|
|
618
1273
|
const arrayMethodHandlers = {
|
|
619
1274
|
push: (obj, args) => `${obj} = append(${obj}, ${args.join(', ')})`,
|
|
620
1275
|
join: (obj, args) => {
|
|
@@ -638,6 +1293,14 @@ function getDynamicCallHandler(caller, objectType) {
|
|
|
638
1293
|
const dotIndex = caller.lastIndexOf('.');
|
|
639
1294
|
if (dotIndex !== -1) {
|
|
640
1295
|
const methodName = caller.substring(dotIndex + 1);
|
|
1296
|
+
// toString() is universal — works for any type including numbers and objects
|
|
1297
|
+
if (methodName === 'toString') {
|
|
1298
|
+
return (c) => {
|
|
1299
|
+
const obj = c.substring(0, dotIndex);
|
|
1300
|
+
importedPackages.add('fmt');
|
|
1301
|
+
return `fmt.Sprintf("%v", ${obj})`;
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
641
1304
|
// Class instances use their own methods — never intercept
|
|
642
1305
|
if (objectType === 'class')
|
|
643
1306
|
return null;
|
|
@@ -648,9 +1311,15 @@ function getDynamicCallHandler(caller, objectType) {
|
|
|
648
1311
|
else if (objectType === 'array') {
|
|
649
1312
|
handler = arrayMethodHandlers[methodName];
|
|
650
1313
|
}
|
|
1314
|
+
else if (objectType === 'RegExp') {
|
|
1315
|
+
handler = regexpMethodHandlers[methodName];
|
|
1316
|
+
}
|
|
651
1317
|
else {
|
|
652
1318
|
// Unknown type: try both maps for backward compatibility
|
|
653
|
-
handler =
|
|
1319
|
+
handler =
|
|
1320
|
+
stringMethodHandlers[methodName] ??
|
|
1321
|
+
arrayMethodHandlers[methodName] ??
|
|
1322
|
+
regexpMethodHandlers[methodName];
|
|
654
1323
|
}
|
|
655
1324
|
if (handler) {
|
|
656
1325
|
return (c, args) => {
|
|
@@ -690,6 +1359,16 @@ function getOperatorText(operator) {
|
|
|
690
1359
|
function getTimerName(name) {
|
|
691
1360
|
return `__timer_${name.replaceAll(' ', '_').replaceAll('"', '')}__`;
|
|
692
1361
|
}
|
|
1362
|
+
function jsRegexFlagsToGo(flags) {
|
|
1363
|
+
let goFlags = '';
|
|
1364
|
+
if (flags.includes('i'))
|
|
1365
|
+
goFlags += 'i';
|
|
1366
|
+
if (flags.includes('m'))
|
|
1367
|
+
goFlags += 'm';
|
|
1368
|
+
if (flags.includes('s'))
|
|
1369
|
+
goFlags += 's';
|
|
1370
|
+
return goFlags ? `(?${goFlags})` : '';
|
|
1371
|
+
}
|
|
693
1372
|
function getTypeParameters(typeParameters) {
|
|
694
1373
|
if (!typeParameters || typeParameters.length === 0)
|
|
695
1374
|
return '';
|
|
@@ -712,6 +1391,59 @@ function getTypeArguments(typeArguments) {
|
|
|
712
1391
|
const args = typeArguments.map((ta) => getType(ta));
|
|
713
1392
|
return `[${args.join(', ')}]`;
|
|
714
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
|
+
}
|
|
715
1447
|
function getSafeName(name) {
|
|
716
1448
|
if (!dangerousNames.has(name)) {
|
|
717
1449
|
return name;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typenative",
|
|
3
|
-
"version": "0.0.
|
|
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",
|
package/types/typenative.d.ts
CHANGED
|
@@ -3,15 +3,16 @@ interface Boolean {}
|
|
|
3
3
|
interface CallableFunction extends Function {}
|
|
4
4
|
|
|
5
5
|
interface Function {
|
|
6
|
-
apply(thisArg: any, argArray?: any): any;
|
|
6
|
+
/* apply(thisArg: any, argArray?: any): any;
|
|
7
7
|
call(thisArg: any, ...argArray: any[]): any;
|
|
8
|
-
bind(thisArg: any, ...argArray: any[]): any;
|
|
8
|
+
bind(thisArg: any, ...argArray: any[]): any; */
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
interface IArguments {
|
|
12
12
|
[index: number]: any;
|
|
13
13
|
length: number;
|
|
14
14
|
}
|
|
15
|
+
|
|
15
16
|
interface NewableFunction extends Function {}
|
|
16
17
|
|
|
17
18
|
interface Number {
|
|
@@ -28,6 +29,12 @@ interface RegExp {
|
|
|
28
29
|
exec(string: string): string[] | null;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
interface RegExpConstructor {
|
|
33
|
+
new (pattern: string, flags?: string): RegExp;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare var RegExp: RegExpConstructor;
|
|
37
|
+
|
|
31
38
|
interface SymbolConstructor {
|
|
32
39
|
readonly iterator: unique symbol;
|
|
33
40
|
}
|
|
@@ -74,6 +81,22 @@ interface Array<T> extends IterableIterator<T> {
|
|
|
74
81
|
* Returns a copy of a section of an array.
|
|
75
82
|
*/
|
|
76
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;
|
|
77
100
|
/**
|
|
78
101
|
* Returns the index of the first occurrence of a value in an array, or -1 if it is not present.
|
|
79
102
|
*/
|