typescript-to-gdscript 0.1.0 → 0.1.1
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/README.md +229 -103
- package/dist/converter/common/index.d.ts +17 -1
- package/dist/converter/common/index.d.ts.map +1 -1
- package/dist/converter/common/index.js +69 -19
- package/dist/converter/common/index.js.map +1 -1
- package/dist/converter/ts-to-gd/class-members.d.ts.map +1 -1
- package/dist/converter/ts-to-gd/class-members.js +5 -5
- package/dist/converter/ts-to-gd/class-members.js.map +1 -1
- package/dist/converter/ts-to-gd/expressions.d.ts.map +1 -1
- package/dist/converter/ts-to-gd/expressions.js +1 -1
- package/dist/converter/ts-to-gd/expressions.js.map +1 -1
- package/dist/converter/ts-to-gd/file-scope.d.ts.map +1 -1
- package/dist/converter/ts-to-gd/file-scope.js +1 -1
- package/dist/converter/ts-to-gd/file-scope.js.map +1 -1
- package/dist/converter/ts-to-gd/gd-getset.js +2 -2
- package/dist/converter/ts-to-gd/gd-getset.js.map +1 -1
- package/dist/converter/ts-to-gd/index.d.ts.map +1 -1
- package/dist/converter/ts-to-gd/index.js +1 -0
- package/dist/converter/ts-to-gd/index.js.map +1 -1
- package/dist/converter/ts-to-gd/parameters.d.ts.map +1 -1
- package/dist/converter/ts-to-gd/parameters.js +2 -2
- package/dist/converter/ts-to-gd/parameters.js.map +1 -1
- package/dist/converter/ts-to-gd/statements.d.ts.map +1 -1
- package/dist/converter/ts-to-gd/statements.js +1 -1
- package/dist/converter/ts-to-gd/statements.js.map +1 -1
- package/dist/typings/override-system.d.ts.map +1 -1
- package/dist/typings/override-system.js +6 -0
- package/dist/typings/override-system.js.map +1 -1
- package/package.json +6 -1
- package/typings/classes/AnimationNodeBlendSpace1D.d.ts +2 -17
- package/typings/classes/AnimationNodeBlendSpace2D.d.ts +2 -17
- package/typings/classes/Area3D.d.ts +7 -14
- package/typings/classes/ArrayMesh.d.ts +0 -15
- package/typings/classes/AudioEffectAmplify.d.ts +2 -2
- package/typings/classes/AudioEffectBandLimitFilter.d.ts +1 -1
- package/typings/classes/AudioEffectBandPassFilter.d.ts +1 -1
- package/typings/classes/AudioEffectCapture.d.ts +9 -10
- package/typings/classes/AudioEffectChorus.d.ts +28 -57
- package/typings/classes/AudioEffectCompressor.d.ts +9 -11
- package/typings/classes/AudioEffectDelay.d.ts +15 -21
- package/typings/classes/AudioEffectDistortion.d.ts +10 -19
- package/typings/classes/AudioEffectEQ.d.ts +2 -2
- package/typings/classes/AudioEffectEQ10.d.ts +2 -2
- package/typings/classes/AudioEffectEQ21.d.ts +2 -2
- package/typings/classes/AudioEffectEQ6.d.ts +2 -2
- package/typings/classes/AudioEffectFilter.d.ts +9 -25
- package/typings/classes/AudioEffectHardLimiter.d.ts +5 -8
- package/typings/classes/AudioEffectHighPassFilter.d.ts +1 -1
- package/typings/classes/AudioEffectHighShelfFilter.d.ts +1 -1
- package/typings/classes/AudioEffectLimiter.d.ts +4 -9
- package/typings/classes/AudioEffectLowPassFilter.d.ts +1 -1
- package/typings/classes/AudioEffectLowShelfFilter.d.ts +1 -1
- package/typings/classes/AudioEffectNotchFilter.d.ts +1 -1
- package/typings/classes/AudioEffectPanner.d.ts +2 -7
- package/typings/classes/AudioEffectPhaser.d.ts +7 -9
- package/typings/classes/AudioEffectPitchShift.d.ts +3 -3
- package/typings/classes/AudioEffectReverb.d.ts +8 -17
- package/typings/classes/AudioEffectSpectrumAnalyzer.d.ts +5 -4
- package/typings/classes/AudioEffectStereoEnhance.d.ts +4 -7
- package/typings/classes/AudioServer.d.ts +2 -2
- package/typings/classes/AudioStreamPlaybackResampled.d.ts +0 -8
- package/typings/classes/BaseButton.d.ts +1 -1
- package/typings/classes/Basis.d.ts +0 -4
- package/typings/classes/Camera3D.d.ts +1 -1
- package/typings/classes/CameraAttributes.d.ts +0 -1
- package/typings/classes/CameraFeed.d.ts +0 -4
- package/typings/classes/CanvasItem.d.ts +1 -1
- package/typings/classes/CodeEdit.d.ts +0 -4
- package/typings/classes/CollisionObject2D.d.ts +0 -6
- package/typings/classes/CollisionPolygon2D.d.ts +0 -5
- package/typings/classes/CollisionShape2D.d.ts +0 -5
- package/typings/classes/Container.d.ts +0 -6
- package/typings/classes/Control.d.ts +0 -59
- package/typings/classes/DPITexture.d.ts +0 -14
- package/typings/classes/Decal.d.ts +0 -1
- package/typings/classes/Dictionary.d.ts +0 -1
- package/typings/classes/DisplayServer.d.ts +1 -128
- package/typings/classes/EditorDock.d.ts +2 -8
- package/typings/classes/EditorExportPlugin.d.ts +0 -5
- package/typings/classes/EditorFileSystem.d.ts +0 -2
- package/typings/classes/EditorInterface.d.ts +2 -4
- package/typings/classes/EditorSettings.d.ts +133 -197
- package/typings/classes/EditorSpinSlider.d.ts +0 -6
- package/typings/classes/Environment.d.ts +2 -7
- package/typings/classes/FileAccess.d.ts +1 -1
- package/typings/classes/FileDialog.d.ts +1 -1
- package/typings/classes/FoldableContainer.d.ts +1 -1
- package/typings/classes/Font.d.ts +2 -2
- package/typings/classes/GPUParticles3D.d.ts +0 -4
- package/typings/classes/GeometryInstance3D.d.ts +1 -1
- package/typings/classes/GradientTexture2D.d.ts +0 -2
- package/typings/classes/Image.d.ts +0 -1
- package/typings/classes/ImageTexture.d.ts +2 -0
- package/typings/classes/ImporterMesh.d.ts +0 -6
- package/typings/classes/Input.d.ts +13 -148
- package/typings/classes/InputEvent.d.ts +0 -8
- package/typings/classes/InputEventGesture.d.ts +3 -2
- package/typings/classes/InputEventMouse.d.ts +3 -2
- package/typings/classes/InputEventWithModifiers.d.ts +3 -2
- package/typings/classes/InputMap.d.ts +0 -3
- package/typings/classes/ItemList.d.ts +2 -9
- package/typings/classes/Joint3D.d.ts +0 -1
- package/typings/classes/LookAtModifier3D.d.ts +1 -2
- package/typings/classes/MainLoop.d.ts +0 -4
- package/typings/classes/NativeMenu.d.ts +3 -10
- package/typings/classes/Node.d.ts +5 -9
- package/typings/classes/OS.d.ts +2 -2
- package/typings/classes/Object.d.ts +2 -5
- package/typings/classes/OptionButton.d.ts +0 -6
- package/typings/classes/PCKPacker.d.ts +0 -4
- package/typings/classes/ParticleProcessMaterial.d.ts +0 -5
- package/typings/classes/PhysicsDirectBodyState2D.d.ts +1 -2
- package/typings/classes/PhysicsDirectBodyState3D.d.ts +1 -2
- package/typings/classes/PhysicsServer2D.d.ts +2 -2
- package/typings/classes/PhysicsServer2DExtension.d.ts +1 -1
- package/typings/classes/PhysicsServer3D.d.ts +42 -145
- package/typings/classes/PopupMenu.d.ts +8 -22
- package/typings/classes/PopupPanel.d.ts +0 -2
- package/typings/classes/PortableCompressedTexture2D.d.ts +2 -0
- package/typings/classes/ProjectSettings.d.ts +60 -44
- package/typings/classes/RDShaderSPIRV.d.ts +0 -30
- package/typings/classes/RDShaderSource.d.ts +0 -10
- package/typings/classes/RenderingDevice.d.ts +0 -104
- package/typings/classes/RenderingServer.d.ts +11 -213
- package/typings/classes/Resource.d.ts +2 -2
- package/typings/classes/ResourceImporterDynamicFont.d.ts +1 -3
- package/typings/classes/ResourceImporterSVG.d.ts +0 -10
- package/typings/classes/ResourceImporterTexture.d.ts +2 -2
- package/typings/classes/SceneTree.d.ts +29 -5
- package/typings/classes/ScriptEditor.d.ts +0 -6
- package/typings/classes/Shader.d.ts +0 -2
- package/typings/classes/Shape3D.d.ts +0 -1
- package/typings/classes/SplitContainer.d.ts +0 -6
- package/typings/classes/String.d.ts +1 -1
- package/typings/classes/SubViewport.d.ts +0 -4
- package/typings/classes/TextEdit.d.ts +0 -3
- package/typings/classes/TextServer.d.ts +0 -2
- package/typings/classes/TextServerExtension.d.ts +0 -2
- package/typings/classes/Texture2D.d.ts +0 -14
- package/typings/classes/TextureRect.d.ts +1 -4
- package/typings/classes/Tree.d.ts +2 -9
- package/typings/classes/TreeItem.d.ts +0 -8
- package/typings/classes/Tween.d.ts +0 -13
- package/typings/classes/Viewport.d.ts +0 -10
- package/typings/classes/VisualShader.d.ts +0 -2
- package/typings/classes/VisualShaderNodeColorFunc.d.ts +2 -2
- package/typings/classes/VoxelGIData.d.ts +0 -10
- package/typings/classes/Window.d.ts +0 -23
- package/typings/classes/XRCamera3D.d.ts +1 -1
- package/typings/classes/XRServer.d.ts +0 -2
- package/typings/classes/_globals.d.ts +86 -45
- package/typings/classes/index.d.ts +0 -5
- package/typings/godot-class-registry.json +108 -1254
- package/typings/classes/AccessibilityServer.d.ts +0 -405
- package/typings/classes/AwaitTweener.d.ts +0 -10
- package/typings/classes/BlitMaterial.d.ts +0 -22
- package/typings/classes/DrawableTexture2D.d.ts +0 -47
- package/typings/classes/VirtualJoystick.d.ts +0 -94
package/README.md
CHANGED
|
@@ -4,8 +4,6 @@ Write Godot 4 scripts in TypeScript. Get autocomplete and type-checking for the
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
> Have an existing GDScript project? Bulk-convert it to TypeScript with [tstogd initial-convert-gd-to-ts](docs/gd-to-ts-migration.md).
|
|
8
|
-
|
|
9
7
|
## Why
|
|
10
8
|
|
|
11
9
|
GDScript is great for getting things on screen, but its type system and tooling are intentionally lightweight. The bigger a project gets, the more that costs you: typos surface at runtime, refactors are risky, and autocomplete only goes so far. TypeScript was built for exactly this problem, and it has had a decade of investment poured into making large codebases safe to change.
|
|
@@ -24,13 +22,55 @@ The output is clean, idiomatic `.gd` you can read and ship — TypeScript is the
|
|
|
24
22
|
|
|
25
23
|
- **Type-safe Godot API** — all 900+ engine classes generated as `.d.ts` from the official Godot XML docs, with nullable reference types where appropriate
|
|
26
24
|
- **Live IDE diagnostics** — TypeScript language service plugin surfaces converter and Godot CLI errors as squiggles, on unsaved buffers
|
|
27
|
-
- **Watch mode** — auto-convert on save, then run a debounced full-project check (TypeScript + converter + Godot CLI)
|
|
25
|
+
- **Watch mode** — auto-convert on save and keep typings in sync (it watches your `.tscn` scenes and assets too), then run a debounced full-project check (TypeScript + converter + Godot CLI)
|
|
28
26
|
- **Source maps** — Godot script parse errors, runtime errors and stack traces map back to your TypeScript line/column
|
|
29
27
|
- **Scene and path typings** — `get_node()`, `get_parent()`, `get_child()`, `load()`, `preload()` and group queries typed from your project files
|
|
30
28
|
- **`gd` namespace** — strongly-typed helpers for GDScript-only constructs (signals, decorators, match, operator overloading)
|
|
31
29
|
- **Addons supported** — typings for third-party GDScript addons, consumable from TypeScript
|
|
32
30
|
- **GD → TS migration** — one-shot bulk converter for existing projects
|
|
33
31
|
|
|
32
|
+
### Showcase
|
|
33
|
+
|
|
34
|
+
<details>
|
|
35
|
+
<summary>Scene and path type hints</summary>
|
|
36
|
+
|
|
37
|
+
Given this scene structure:
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
You get type hints for this:
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
…or this:
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
…or even this:
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
`preload()` is typed too:
|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
<details>
|
|
60
|
+
<summary>GDScript and converter errors in TypeScript code</summary>
|
|
61
|
+
|
|
62
|
+
GDScript doesn't allow a static and an instance member to share the same name — though TypeScript does. Your IDE surfaces the GDScript error at the exact spot in your `.ts`:
|
|
63
|
+
|
|
64
|
+

|
|
65
|
+
|
|
66
|
+
For edge cases — like using the result of `&&` as a value — you get a converter error instead, because logical operations in GDScript always return a boolean:
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
|
|
70
|
+
You also get all of these errors (along with the TypeScript errors themselves) from the `convert` CLI command.
|
|
71
|
+
|
|
72
|
+
</details>
|
|
73
|
+
|
|
34
74
|
## Quick start
|
|
35
75
|
|
|
36
76
|
```bash
|
|
@@ -41,10 +81,12 @@ tstogd init # interactive: writes tstogd.json + tsconfig.json + installs typescr
|
|
|
41
81
|
Then:
|
|
42
82
|
|
|
43
83
|
1. Write a `.ts` class in your TS source dir (default `src/`).
|
|
44
|
-
2. `tstogd convert` — emits `.gd`
|
|
84
|
+
2. `tstogd convert` — emits `.gd` into your output dir (default `scripts/`), regenerates all typings, and runs a full diagnostic check.
|
|
45
85
|
3. Attach the `.gd` file in Godot as you normally would. Configure Godot's external editor to point at your IDE for TS language server and one-click jump-to-source ([guide](docs/ide-integration.md)).
|
|
46
86
|
|
|
47
|
-
|
|
87
|
+
**That's the whole loop — `convert` and `watch` are the only commands you need in normal use.** Each one converts your code _and_ regenerates every typing (scene, script, resource, addon) in the same pass, so `get_node()` paths, `res://` references, and group queries stay in sync without any extra step. Most projects never run another CLI command.
|
|
88
|
+
|
|
89
|
+
For day-to-day work, run **`tstogd watch`**: it auto-converts on save, watches your `.tscn` scenes and assets so scene typings refresh the instant you change the tree in Godot, and runs a debounced full-project check feeding live IDE diagnostics.
|
|
48
90
|
|
|
49
91
|
### Requirements
|
|
50
92
|
|
|
@@ -56,19 +98,30 @@ Use `tstogd watch` during development for auto-conversion plus live diagnostics.
|
|
|
56
98
|
|
|
57
99
|
Setup `tstogd.json` and `tsconfig.json` in your project - for full template and field reference see [docs/configuration.md](docs/configuration.md).
|
|
58
100
|
|
|
101
|
+
### Migration from existing GDScript
|
|
102
|
+
|
|
103
|
+
Have an existing GDScript project? Bulk-convert it to TypeScript with [tstogd initial-convert-gd-to-ts](docs/gd-to-ts-migration.md).
|
|
104
|
+
|
|
59
105
|
## CLI
|
|
60
106
|
|
|
107
|
+
In everyday use you only need these three:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
tstogd init # one-time interactive scaffold (tsconfig + tstogd.json + ts-plugin)
|
|
111
|
+
tstogd convert # convert TS → GD + regenerate all typings + full diagnostic check
|
|
112
|
+
tstogd watch # the same, continuously: auto-convert on save, live diagnostics
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`convert` and `watch` already regenerate every typing (scene, script, resource, addon) as part of the run — there is no separate "generate typings" step to remember. The commands below cover one-off or advanced situations, and most projects never touch them:
|
|
116
|
+
|
|
61
117
|
```bash
|
|
62
|
-
tstogd
|
|
63
|
-
tstogd
|
|
64
|
-
tstogd watch # auto-convert on change + debounced full check
|
|
65
|
-
tstogd generate-typings # generate scene + script typings
|
|
66
|
-
tstogd clear-cache # clear conversion cache
|
|
118
|
+
tstogd generate-typings # regenerate typings standalone (convert/watch do this for you)
|
|
119
|
+
tstogd clear-cache # clear the conversion cache (e.g. after upgrading the converter)
|
|
67
120
|
```
|
|
68
121
|
|
|
69
122
|
Full reference in [docs/cli.md](docs/cli.md). Specialized commands:
|
|
70
123
|
|
|
71
|
-
- [`initial-convert-gd-to-ts`](docs/gd-to-ts-migration.md) — bulk migration of an existing GDScript project to TypeScript
|
|
124
|
+
- [`initial-convert-gd-to-ts`](docs/gd-to-ts-migration.md) — one-shot bulk migration of an existing GDScript project to TypeScript
|
|
72
125
|
- [`generate-gdscript-global-typings`](docs/typings.md#tstogd-generate-gdscript-global-typings), [`generate-addon-typings`](docs/typings.md#tstogd-generate-addon-typings) — typings generation
|
|
73
126
|
- [`open-editor`](docs/ide-integration.md#tstogd-open-editor) — Godot external-editor integration
|
|
74
127
|
|
|
@@ -86,123 +139,114 @@ Each `.ts` file must contain exactly one class. Named classes (`class Foo`) are
|
|
|
86
139
|
- `let` and `const` both convert to GDScript `var`. **Use `let`** — TS `var` is restricted (GDScript `var` ≈ TS `let`, not TS `var`).
|
|
87
140
|
- `undefined` is restricted — use `null`. Optional property access (`obj.foo` where the type includes `undefined`) auto-converts to `obj.get("foo")`. Same for `obj["key"]` with an undefined type. Class-field access stays direct.
|
|
88
141
|
- `TSOnly<T>` — type-level wrapper, stripped at conversion.
|
|
142
|
+
- Only types GDScript actually has get an annotation. Godot classes/value types and your own `class_name` classes (including ones imported from another file) keep their type; plain `interface`s, `object`, `type` aliases, and unknown/unresolved names have the annotation **omitted** (bare untyped `var x` / `func f(x)`) rather than emitting a bogus GD type. See [docs/transform-rules.md#type-annotations--what-gets-emitted](docs/transform-rules.md#type-annotations--what-gets-emitted).
|
|
89
143
|
|
|
90
144
|
For comments, async, `this` / `self`, logical operators, and constructor mapping, see [docs/transform-rules.md](docs/transform-rules.md).
|
|
91
145
|
|
|
92
146
|
## Cheat sheet
|
|
93
147
|
|
|
94
|
-
|
|
148
|
+
Basics for writing scripts For advanced helpers and the deep dive, see the [**full cheat sheet** in docs/transform-rules.md](docs/transform-rules.md#full-cheat-sheet).
|
|
95
149
|
|
|
96
150
|
```typescript
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// class-level
|
|
151
|
+
// Use imports instead of global classes for better DX
|
|
152
|
+
// Can be changed by `converterOptions.generateGlobalClassTypes` config option
|
|
153
|
+
import { PlayerSprite, type PlayerSpriteState } from './PlayerSprite';
|
|
154
|
+
|
|
155
|
+
// Inner classes, class-level constants & enums.
|
|
156
|
+
// A `namespace Player` merged with class `Player`
|
|
157
|
+
// lifts each exported member onto the class.
|
|
102
158
|
export namespace Player {
|
|
103
|
-
export const MAX_HP = 100;
|
|
159
|
+
export const MAX_HP = 100;
|
|
104
160
|
|
|
105
161
|
export enum State {
|
|
106
162
|
IDLE,
|
|
107
163
|
RUNNING,
|
|
108
|
-
}
|
|
164
|
+
}
|
|
109
165
|
|
|
110
166
|
export class Bullet extends Node2D {
|
|
111
|
-
|
|
112
|
-
damage: int = 10; // var damage: int = 10
|
|
167
|
+
damage: int = 10;
|
|
113
168
|
}
|
|
114
169
|
}
|
|
115
170
|
|
|
116
171
|
export class Player extends CharacterBody2D {
|
|
117
|
-
//
|
|
118
|
-
static MAX_INSTANCES = 8;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
@
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// ── Constructor → _init ──────────────────────────────────────────
|
|
172
|
+
// Statics (mutable); for constants use the namespace
|
|
173
|
+
static MAX_INSTANCES = 8;
|
|
174
|
+
|
|
175
|
+
// Field decorators. `@exports` aliases `@export`
|
|
176
|
+
// (a TS reserved word); other annotations are 1:1
|
|
177
|
+
@exports speed: float = 200.0;
|
|
178
|
+
@export_range(0, 100) health: int = 100;
|
|
179
|
+
@onready sprite: PlayerSprite = this.get_node('Sprite');
|
|
180
|
+
@onready sprite_state: PlayerSpriteState = this.sprite.get_state();
|
|
181
|
+
|
|
182
|
+
// Signals; tuple labels become GD arg names
|
|
183
|
+
health_changed = gd.signal<[from: int, to: int]>();
|
|
184
|
+
died = gd.signal();
|
|
185
|
+
|
|
186
|
+
// Constructor replaces _init
|
|
134
187
|
constructor(speed: float = 200.0) {
|
|
135
|
-
|
|
136
|
-
this.speed = speed; // self.speed = speed
|
|
188
|
+
this.speed = speed;
|
|
137
189
|
}
|
|
138
190
|
|
|
139
|
-
//
|
|
191
|
+
// Getters / setters (native TS accessors)
|
|
140
192
|
get score(): int {
|
|
141
193
|
return this.score;
|
|
142
|
-
}
|
|
194
|
+
}
|
|
143
195
|
set score(v: int) {
|
|
144
196
|
this.score = v;
|
|
145
|
-
}
|
|
146
|
-
// set(value): score = value
|
|
197
|
+
}
|
|
147
198
|
|
|
148
|
-
//
|
|
199
|
+
// Async / await; Promise<T> unwraps to T
|
|
149
200
|
async long_task(): Promise<int> {
|
|
150
|
-
// → func long_task() -> int:
|
|
151
201
|
await this.get_tree().create_timer(1).timeout;
|
|
152
202
|
return 42;
|
|
153
203
|
}
|
|
154
204
|
|
|
155
205
|
_process(delta: float) {
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
let
|
|
159
|
-
let ternary = eq ? 1 : 2; // → var ternary = 1 if eq else 2
|
|
206
|
+
// Operators
|
|
207
|
+
let eq = this.health === 0;
|
|
208
|
+
let ternary = eq ? 1 : 2;
|
|
160
209
|
|
|
161
|
-
//
|
|
162
|
-
let pos = gd.ops.add(this.position, Vector2(1, 0));
|
|
163
|
-
//
|
|
210
|
+
// Math on value types (Vector2, Color, …) needs gd.ops
|
|
211
|
+
let pos = gd.ops.add(this.position, Vector2(1, 0));
|
|
212
|
+
// Also available: add, sub, mul, div, rem, eq, ne, gt, gte, lt, lte, plus (unary), minus (unary)
|
|
164
213
|
|
|
165
|
-
//
|
|
166
|
-
|
|
214
|
+
// Type casting / checking
|
|
215
|
+
// self.get_node("Body") as CharacterBody2D
|
|
216
|
+
let body = gd.as(this.get_node('Body'), CharacterBody2D);
|
|
167
217
|
if (body instanceof CharacterBody2D) {
|
|
168
|
-
/*
|
|
169
|
-
}
|
|
218
|
+
/* body is CharacterBody2D */
|
|
219
|
+
}
|
|
170
220
|
if (gd.is(this.health, int)) {
|
|
171
|
-
/*
|
|
172
|
-
}
|
|
221
|
+
/* health is int */
|
|
222
|
+
}
|
|
173
223
|
|
|
174
|
-
//
|
|
175
|
-
let bullet = new Player.Bullet();
|
|
224
|
+
// Object construction instead of Player.Bullet.new()
|
|
225
|
+
let bullet = new Player.Bullet();
|
|
176
226
|
|
|
177
|
-
//
|
|
178
|
-
let stats = { name: 'Hero', hp: 100 };
|
|
227
|
+
// Dictionaries (other keys: gd.dict)
|
|
228
|
+
let stats = { name: 'Hero', hp: 100 };
|
|
179
229
|
|
|
180
|
-
//
|
|
181
|
-
let greet = `Hi ${this.name}!`;
|
|
182
|
-
let path = NodePath('Body/Sprite');
|
|
183
|
-
let sig = StringName('died');
|
|
230
|
+
// Strings, StringName, NodePath
|
|
231
|
+
let greet = `Hi ${this.name}!`;
|
|
232
|
+
let path = NodePath('Body/Sprite');
|
|
233
|
+
let sig = StringName('died');
|
|
184
234
|
|
|
185
|
-
//
|
|
186
|
-
for (let s of range(3)) print(s);
|
|
235
|
+
// Control flow
|
|
236
|
+
for (let s of range(3)) print(s);
|
|
187
237
|
|
|
188
|
-
//
|
|
189
|
-
switch (
|
|
190
|
-
this.health // → match self.health:
|
|
191
|
-
) {
|
|
238
|
+
// Match (TS switch round-trips with GD match)
|
|
239
|
+
switch (this.health) {
|
|
192
240
|
case 0:
|
|
193
241
|
this.died.emit();
|
|
194
|
-
break; // 0: self.died.emit()
|
|
195
242
|
default:
|
|
196
243
|
print('alive');
|
|
197
|
-
break; // _: print("alive")
|
|
198
244
|
}
|
|
199
245
|
}
|
|
200
246
|
|
|
201
247
|
_ready() {
|
|
202
|
-
// → func _ready():
|
|
203
|
-
// Signals — emit and connect
|
|
204
|
-
this.health_changed.emit(0, 100); // → self.health_changed.emit(0, 100)
|
|
205
248
|
this.health_changed.connect(this._on_health_changed);
|
|
249
|
+
this.health_changed.emit(0, 100);
|
|
206
250
|
}
|
|
207
251
|
|
|
208
252
|
_on_health_changed(from: int, to: int) {
|
|
@@ -214,35 +258,117 @@ export class Player extends CharacterBody2D {
|
|
|
214
258
|
}
|
|
215
259
|
```
|
|
216
260
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
261
|
+
<details>
|
|
262
|
+
<summary>Result gdscript:</summary>
|
|
263
|
+
|
|
264
|
+
```gdscript
|
|
265
|
+
extends CharacterBody2D
|
|
266
|
+
class_name Player
|
|
267
|
+
|
|
268
|
+
const MAX_HP = 100
|
|
269
|
+
|
|
270
|
+
enum State { IDLE, RUNNING }
|
|
271
|
+
|
|
272
|
+
class Bullet extends Node2D:
|
|
273
|
+
var damage: int = 10
|
|
274
|
+
|
|
275
|
+
# Statics (mutable); for constants use the namespace
|
|
276
|
+
static var MAX_INSTANCES = 8
|
|
277
|
+
# Field decorators. `@exports` aliases `@export`
|
|
278
|
+
# (a TS reserved word); other annotations are 1:1
|
|
279
|
+
@export
|
|
280
|
+
var speed: float = 200.0
|
|
281
|
+
@export_range(0, 100)
|
|
282
|
+
var health: int = 100
|
|
283
|
+
@onready
|
|
284
|
+
var sprite: PlayerSprite = self.get_node("Sprite")
|
|
285
|
+
@onready
|
|
286
|
+
var spriteState = self.sprite.get_state()
|
|
287
|
+
# Signals; tuple labels become GD arg names
|
|
288
|
+
signal health_changed(from: int, to: int)
|
|
289
|
+
signal died
|
|
290
|
+
|
|
291
|
+
# Constructor replaces _init
|
|
292
|
+
func _init(speed: float = 200.0):
|
|
293
|
+
self.speed = speed
|
|
294
|
+
|
|
295
|
+
var score: int:
|
|
296
|
+
get:
|
|
297
|
+
return score
|
|
298
|
+
set(v):
|
|
299
|
+
score = v
|
|
300
|
+
|
|
301
|
+
# Async / await; Promise<T> unwraps to T
|
|
302
|
+
func long_task() -> int:
|
|
303
|
+
await self.get_tree().create_timer(1).timeout
|
|
304
|
+
return 42
|
|
305
|
+
|
|
306
|
+
func _process(delta: float):
|
|
307
|
+
# Operators
|
|
308
|
+
var eq = self.health == 0
|
|
309
|
+
var ternary = 1 if eq else 2
|
|
310
|
+
# Math on value types (Vector2, Color, …) needs gd.ops
|
|
311
|
+
var pos = (self.position + Vector2(1, 0))
|
|
312
|
+
# Also available: add, sub, mul, div, rem, eq, ne, gt, gte, lt, lte, plus (unary), minus (unary)
|
|
313
|
+
# Type casting / checking
|
|
314
|
+
# self.get_node("Body") as CharacterBody2D
|
|
315
|
+
var body = self.get_node("Body") as CharacterBody2D
|
|
316
|
+
if body is CharacterBody2D:
|
|
317
|
+
pass
|
|
318
|
+
if self.health is int:
|
|
319
|
+
pass
|
|
320
|
+
# Object construction instead of Player.Bullet.new()
|
|
321
|
+
var bullet = self.Bullet.new()
|
|
322
|
+
# Dictionaries (other keys: gd.dict)
|
|
323
|
+
var stats = {
|
|
324
|
+
"name": "Hero",
|
|
325
|
+
"hp": 100,
|
|
326
|
+
}
|
|
327
|
+
# Strings, StringName, NodePath
|
|
328
|
+
var greet = "Hi " + str(self.name) + "!"
|
|
329
|
+
var path = NodePath("Body/Sprite")
|
|
330
|
+
var sig = StringName("died")
|
|
331
|
+
# Control flow
|
|
332
|
+
for s in range(3):
|
|
333
|
+
print(s)
|
|
334
|
+
# Match (TS switch round-trips with GD match)
|
|
335
|
+
match self.health:
|
|
336
|
+
0:
|
|
337
|
+
self.died.emit()
|
|
338
|
+
_:
|
|
339
|
+
print("alive")
|
|
340
|
+
|
|
341
|
+
func _ready():
|
|
342
|
+
self.health_changed.connect(self._on_health_changed)
|
|
343
|
+
self.health_changed.emit(0, 100)
|
|
344
|
+
|
|
345
|
+
func _on_health_changed(from: int, to: int):
|
|
346
|
+
pass
|
|
347
|
+
|
|
348
|
+
func die():
|
|
349
|
+
pass
|
|
350
|
+
```
|
|
220
351
|
|
|
221
|
-
|
|
222
|
-
| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
223
|
-
| `GodotResources` | every asset path (`.tscn`, `.gd`, `.tres`, images, audio, …) | The resource type the path resolves to — `PackedScene<Root>` for scenes, `typeof Class` for scripts, the concrete `Resource` subclass for `.tres` | `let mat: GodotResources["res://player_material.tres"]` |
|
|
224
|
-
| `GodotScripts` | `.gd` script paths | `typeof <Class>` — the class constructor for the script | `const GameManager: GodotScripts["res://GameManager.gd"]` |
|
|
225
|
-
| `GodotScenes` | `.tscn` paths | The **root node** type of the scene (with full tree attached) | `let root: GodotScenes["res://Level.tscn"]` |
|
|
226
|
-
| `GodotSceneTrees` | `.tscn` paths | The **tree** type — used when you need to index descendants directly | `type Tree = GodotSceneTrees["res://Player.tscn"]` |
|
|
227
|
-
| `GodotGroups` | group names declared in `.tscn` files | `{ [scenePath]: union of node types in that group }` | `GodotGroups["enemies"]["res://Level.tscn"]` → `Goblin \| Slime` |
|
|
228
|
-
| `GodotConnections` | scene paths that have `[connection]` entries | `{ "Node.signal": _GDSignalConnection<…> }` — connection metadata | `GodotConnections["res://UI.tscn"]["Button.pressed"]` |
|
|
352
|
+
</details>
|
|
229
353
|
|
|
230
|
-
|
|
354
|
+
#### Useful global types
|
|
231
355
|
|
|
232
|
-
|
|
356
|
+
Each interface maps a string literal (a `res://` path or group name) to the type it resolves to — index it directly, e.g. `GodotResources["res://player.tres"]` or `GodotScripts["res://player.gd"]`:
|
|
233
357
|
|
|
234
|
-
|
|
358
|
+
- **`GodotResources`** — any asset path → its resource type (`PackedScene<Root>`, `typeof Class`, or the concrete `Resource` subclass)
|
|
359
|
+
- **`GodotResourceName`** (= `keyof GodotResources`) — any `res://…` asset
|
|
360
|
+
- **`GodotScripts`** — `.gd` path → `typeof <Class>` (the script constructor)
|
|
361
|
+
- **`GodotScriptName`** (= `keyof GodotScripts`) — a `.gd` path
|
|
362
|
+
- **`GodotScenes`** — `.tscn` path → the scene's **root node** type
|
|
363
|
+
- **`GodotSceneName`** (= `keyof GodotScenes`) — a `.tscn` path
|
|
364
|
+
- **`GodotSceneTrees`** — `.tscn` path → the **tree** type, for indexing descendants directly
|
|
365
|
+
- **`GodotSceneTreeName`** (= `keyof GodotSceneTrees`) — same set as `GodotSceneName`, for the tree rather than the root
|
|
366
|
+
- **`GodotGroups`** — group name → union of the node types in that group
|
|
367
|
+
- **`GodotGroupName`** (= `keyof GodotGroups`) — a group identifier (`"enemies"`, …)
|
|
235
368
|
|
|
236
|
-
|
|
237
|
-
| -------------------------- | ------------------------ | ----------------------------------------------------------------------- |
|
|
238
|
-
| `GodotResourceName` | `keyof GodotResources` | `res://…` to any asset |
|
|
239
|
-
| `GodotSceneName` | `keyof GodotScenes` | `res://…` to a `.tscn` |
|
|
240
|
-
| `GodotSceneTreeName` | `keyof GodotSceneTrees` | Same set as `GodotSceneName`; pick when you want the tree, not the root |
|
|
241
|
-
| `GodotScriptName` | `keyof GodotScripts` | `res://…` to a `.gd` |
|
|
242
|
-
| `GodotGroupName` | `keyof GodotGroups` | Group identifier (`"enemies"`, `"entities"`, …) |
|
|
243
|
-
| `GodotConnectionSceneName` | `keyof GodotConnections` | Scene paths that have `[connection]` entries |
|
|
369
|
+
The Godot-typed `load()`, `preload()`, `get_node()`, and autoload singletons all index these under the hood.
|
|
244
370
|
|
|
245
|
-
|
|
371
|
+
That types are resolved lazily, so each alias automatically picks up new entries as more files are converted. Typos in resource paths or group names become compile-time errors instead of silent runtime failures:
|
|
246
372
|
|
|
247
373
|
```typescript
|
|
248
374
|
class AssetLoader extends Node {
|
|
@@ -256,9 +382,9 @@ class AssetLoader extends Node {
|
|
|
256
382
|
|
|
257
383
|
_ready() {
|
|
258
384
|
this.preload_asset('res://Player.tscn'); // ✅ typed
|
|
259
|
-
this.preload_asset('res://does_not_exist.png'); // ❌
|
|
385
|
+
this.preload_asset('res://does_not_exist.png'); // ❌ error
|
|
260
386
|
this.count_in_group('enemies'); // ✅ typed
|
|
261
|
-
this.count_in_group('typo'); // ❌
|
|
387
|
+
this.count_in_group('typo'); // ❌ error
|
|
262
388
|
}
|
|
263
389
|
}
|
|
264
390
|
```
|
|
@@ -40,6 +40,15 @@ export interface TransformContext {
|
|
|
40
40
|
* `extends "res://..."` are taken relative to this directory.
|
|
41
41
|
*/
|
|
42
42
|
projectRoot: string;
|
|
43
|
+
/**
|
|
44
|
+
* Godot class registry. Used to recognise Godot built-in types (classes,
|
|
45
|
+
* value-type constructors, global enums) by name when classifying TS type
|
|
46
|
+
* annotations — see {@link tsTypeNodeToGdType}. May be `undefined` when the
|
|
47
|
+
* registry could not be resolved, in which case classification can only
|
|
48
|
+
* emit types it proves are classes/enums via the checker and drops every
|
|
49
|
+
* other name (Godot built-ins can no longer be recognised by name).
|
|
50
|
+
*/
|
|
51
|
+
registry?: GodotClassRegistry;
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
54
|
* Sentinel TS class name used for anonymous addon scripts (a `.gd`
|
|
@@ -147,12 +156,19 @@ export declare function isFloatType(type: ts.Type, checker: ts.TypeChecker): boo
|
|
|
147
156
|
/**
|
|
148
157
|
* Converts a TypeScript type to its GDScript type annotation string.
|
|
149
158
|
* Returns null if the type should be omitted.
|
|
159
|
+
*
|
|
160
|
+
* NOTE: unlike {@link tsTypeNodeToGdType}, this `ts.Type`-based variant does
|
|
161
|
+
* NOT classify reference types — it returns the symbol name verbatim for any
|
|
162
|
+
* class-like type, so an `object`/`interface`/unknown type would leak a bogus
|
|
163
|
+
* annotation. It is currently unused for emission (kept only for its own
|
|
164
|
+
* recursion). Do not wire it into an emit path without adding the same
|
|
165
|
+
* class/enum/registry classification `classifyTypeReferenceName` applies.
|
|
150
166
|
*/
|
|
151
167
|
export declare function tsTypeToGdType(type: ts.Type, checker: ts.TypeChecker): string | null;
|
|
152
168
|
/**
|
|
153
169
|
* Converts a TypeScript type annotation node to GDScript type string.
|
|
154
170
|
*/
|
|
155
|
-
export declare function tsTypeNodeToGdType(typeNode: ts.TypeNode | undefined, checker: ts.TypeChecker, sourceFile: ts.SourceFile, className?: string): string | null;
|
|
171
|
+
export declare function tsTypeNodeToGdType(typeNode: ts.TypeNode | undefined, checker: ts.TypeChecker, sourceFile: ts.SourceFile, className?: string, registry?: GodotClassRegistry): string | null;
|
|
156
172
|
/**
|
|
157
173
|
* True when `gdType` refers to a Godot class (Node, Resource, Material, user
|
|
158
174
|
* class, ...) — a type that can legitimately hold `null` in GDScript. False
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/converter/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,2FAA2F;IAC3F,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,uEAAuE;IACvE,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,yFAAyF;IACzF,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yBAAyB;IACzB,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC;IACpB,8BAA8B;IAC9B,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC;IACxB,4CAA4C;IAC5C,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC;IAC1B,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,yFAAyF;IACzF,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/converter/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,2FAA2F;IAC3F,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,uEAAuE;IACvE,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,yFAAyF;IACzF,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yBAAyB;IACzB,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC;IACpB,8BAA8B;IAC9B,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC;IACxB,4CAA4C;IAC5C,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC;IAC1B,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,yFAAyF;IACzF,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAID;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B,cAAc,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvE;AAkBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAIrE;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAE1E;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAE1E;AAED,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,GAAG,OAAO,CAGzE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,GAAG,OAAO,CAG3E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,EAAE,CAAC,WAAW,GACtB,MAAM,GAAG,IAAI,CAsDf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,EAAE,CAAC,QAAQ,GAAG,SAAS,EACjC,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,UAAU,EAAE,EAAE,CAAC,UAAU,EACzB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,kBAAkB,GAC5B,MAAM,GAAG,IAAI,CA6Ef;AAoFD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CA2BT"}
|
|
@@ -107,6 +107,13 @@ export function isFloatType(type, checker) {
|
|
|
107
107
|
/**
|
|
108
108
|
* Converts a TypeScript type to its GDScript type annotation string.
|
|
109
109
|
* Returns null if the type should be omitted.
|
|
110
|
+
*
|
|
111
|
+
* NOTE: unlike {@link tsTypeNodeToGdType}, this `ts.Type`-based variant does
|
|
112
|
+
* NOT classify reference types — it returns the symbol name verbatim for any
|
|
113
|
+
* class-like type, so an `object`/`interface`/unknown type would leak a bogus
|
|
114
|
+
* annotation. It is currently unused for emission (kept only for its own
|
|
115
|
+
* recursion). Do not wire it into an emit path without adding the same
|
|
116
|
+
* class/enum/registry classification `classifyTypeReferenceName` applies.
|
|
110
117
|
*/
|
|
111
118
|
export function tsTypeToGdType(type, checker) {
|
|
112
119
|
// Check for int/float aliases
|
|
@@ -165,7 +172,7 @@ export function tsTypeToGdType(type, checker) {
|
|
|
165
172
|
/**
|
|
166
173
|
* Converts a TypeScript type annotation node to GDScript type string.
|
|
167
174
|
*/
|
|
168
|
-
export function tsTypeNodeToGdType(typeNode, checker, sourceFile, className) {
|
|
175
|
+
export function tsTypeNodeToGdType(typeNode, checker, sourceFile, className, registry) {
|
|
169
176
|
if (!typeNode)
|
|
170
177
|
return null;
|
|
171
178
|
// Check for literal type names like `int` and `float`
|
|
@@ -191,23 +198,9 @@ export function tsTypeNodeToGdType(typeNode, checker, sourceFile, className) {
|
|
|
191
198
|
return null;
|
|
192
199
|
if (arg.kind === ts.SyntaxKind.VoidKeyword)
|
|
193
200
|
return null;
|
|
194
|
-
return tsTypeNodeToGdType(arg, checker, sourceFile, className);
|
|
195
|
-
}
|
|
196
|
-
// Type aliases (non-class) → omit annotation. GDScript has no equivalent
|
|
197
|
-
// and `Variant` conveys nothing beyond "untyped", which the bare `var x`
|
|
198
|
-
// / `func f(x)` forms already express.
|
|
199
|
-
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
200
|
-
if (symbol) {
|
|
201
|
-
const declarations = symbol.getDeclarations();
|
|
202
|
-
if (declarations && declarations.length > 0) {
|
|
203
|
-
const decl = declarations[0];
|
|
204
|
-
if (ts.isTypeAliasDeclaration(decl)) {
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
201
|
+
return tsTypeNodeToGdType(arg, checker, sourceFile, className, registry);
|
|
208
202
|
}
|
|
209
|
-
|
|
210
|
-
return name;
|
|
203
|
+
return classifyTypeReferenceName(typeNode, name, checker, registry);
|
|
211
204
|
}
|
|
212
205
|
// Keyword types
|
|
213
206
|
if (typeNode.kind === ts.SyntaxKind.NumberKeyword)
|
|
@@ -228,7 +221,7 @@ export function tsTypeNodeToGdType(typeNode, checker, sourceFile, className) {
|
|
|
228
221
|
return null;
|
|
229
222
|
// Array type: T[] -> Array[T]
|
|
230
223
|
if (ts.isArrayTypeNode(typeNode)) {
|
|
231
|
-
const elementType = tsTypeNodeToGdType(typeNode.elementType, checker, sourceFile, className);
|
|
224
|
+
const elementType = tsTypeNodeToGdType(typeNode.elementType, checker, sourceFile, className, registry);
|
|
232
225
|
return elementType ? `Array[${elementType}]` : 'Array';
|
|
233
226
|
}
|
|
234
227
|
// Function type: () => void -> Callable
|
|
@@ -243,7 +236,7 @@ export function tsTypeNodeToGdType(typeNode, checker, sourceFile, className) {
|
|
|
243
236
|
if (ts.isUnionTypeNode(typeNode)) {
|
|
244
237
|
const nonNull = typeNode.types.filter((t) => !isNullLiteralTypeNode(t));
|
|
245
238
|
if (nonNull.length === 1 && nonNull.length !== typeNode.types.length) {
|
|
246
|
-
return tsTypeNodeToGdType(nonNull[0], checker, sourceFile, className);
|
|
239
|
+
return tsTypeNodeToGdType(nonNull[0], checker, sourceFile, className, registry);
|
|
247
240
|
}
|
|
248
241
|
return null;
|
|
249
242
|
}
|
|
@@ -252,6 +245,63 @@ export function tsTypeNodeToGdType(typeNode, checker, sourceFile, className) {
|
|
|
252
245
|
}
|
|
253
246
|
return null;
|
|
254
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Classify a TS type-reference name and decide whether it should be emitted
|
|
250
|
+
* as a GDScript type annotation.
|
|
251
|
+
*
|
|
252
|
+
* Only types that GDScript actually has are emitted:
|
|
253
|
+
* - User / Godot `class` declarations and `enum` declarations (resolved via
|
|
254
|
+
* the TS checker — user classes resolve locally even without the Godot
|
|
255
|
+
* typings loaded).
|
|
256
|
+
* - Godot built-in types recognised *by name* from the registry: classes
|
|
257
|
+
* (`Node`, `Node2D`, …), value-type constructors (`Vector2`, `Color`,
|
|
258
|
+
* `Dictionary`, …) and global enums (`Key`, `MouseButton`, …). The
|
|
259
|
+
* name-based check is what keeps Godot types working in test/program
|
|
260
|
+
* setups that don't load the Godot `.d.ts` typings.
|
|
261
|
+
*
|
|
262
|
+
* Everything else — type aliases, plain interfaces, `object`-like types,
|
|
263
|
+
* dotted refs that don't resolve to a class/enum (`Node.ProcessMode`,
|
|
264
|
+
* `Outer.SomeIface`) and unknown / unresolved names — is omitted (the bare
|
|
265
|
+
* `var x` / `func f(x)` form is the idiomatic "untyped" GD). GD type hints are
|
|
266
|
+
* optional, so dropping an unverifiable type is always safe; emitting a bogus
|
|
267
|
+
* one would break the `.gd`.
|
|
268
|
+
*
|
|
269
|
+
* Omitting is always safe; emitting an annotation GDScript doesn't recognise
|
|
270
|
+
* breaks the generated `.gd`. So when no registry is available the converter
|
|
271
|
+
* can't recognise Godot built-ins by name — it only emits types it can prove
|
|
272
|
+
* are classes/enums (via the checker) and drops the rest, rather than risk
|
|
273
|
+
* leaking an invalid type.
|
|
274
|
+
*/
|
|
275
|
+
function classifyTypeReferenceName(typeNode, name, checker, registry) {
|
|
276
|
+
// Resolve the symbol, following import aliases to the real declaration so
|
|
277
|
+
// that types imported from another file are classified by what they are,
|
|
278
|
+
// not by the `ImportSpecifier` binding.
|
|
279
|
+
let symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
280
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
281
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
282
|
+
}
|
|
283
|
+
const declarations = symbol?.getDeclarations() ?? [];
|
|
284
|
+
// Type aliases have no GDScript equivalent → omit.
|
|
285
|
+
if (declarations.some(ts.isTypeAliasDeclaration))
|
|
286
|
+
return null;
|
|
287
|
+
// User / Godot `class` and `enum` declarations are valid GD types.
|
|
288
|
+
if (declarations.some((d) => ts.isClassDeclaration(d) || ts.isEnumDeclaration(d))) {
|
|
289
|
+
return name;
|
|
290
|
+
}
|
|
291
|
+
// Godot built-in types are recognised by name (works without typings).
|
|
292
|
+
if (registry &&
|
|
293
|
+
(registry.hasClass(name) ||
|
|
294
|
+
registry.isConstructor(name) ||
|
|
295
|
+
registry.isGlobalEnum(name))) {
|
|
296
|
+
return name;
|
|
297
|
+
}
|
|
298
|
+
// Whatever is left is a non-class type (interface, `object`-like, namespace),
|
|
299
|
+
// a dotted ref we can't verify (`Node.ProcessMode`, `Outer.SomeIface`), or an
|
|
300
|
+
// unknown / unresolved name — none provably a GD type. Omit the annotation:
|
|
301
|
+
// GD type hints are optional, so dropping a type is always safe, whereas
|
|
302
|
+
// emitting a bogus one breaks the generated GDScript.
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
255
305
|
/** True for the `null` keyword wrapped in a `LiteralTypeNode`. */
|
|
256
306
|
function isNullLiteralTypeNode(node) {
|
|
257
307
|
return (ts.isLiteralTypeNode(node) &&
|