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.
Files changed (158) hide show
  1. package/README.md +229 -103
  2. package/dist/converter/common/index.d.ts +17 -1
  3. package/dist/converter/common/index.d.ts.map +1 -1
  4. package/dist/converter/common/index.js +69 -19
  5. package/dist/converter/common/index.js.map +1 -1
  6. package/dist/converter/ts-to-gd/class-members.d.ts.map +1 -1
  7. package/dist/converter/ts-to-gd/class-members.js +5 -5
  8. package/dist/converter/ts-to-gd/class-members.js.map +1 -1
  9. package/dist/converter/ts-to-gd/expressions.d.ts.map +1 -1
  10. package/dist/converter/ts-to-gd/expressions.js +1 -1
  11. package/dist/converter/ts-to-gd/expressions.js.map +1 -1
  12. package/dist/converter/ts-to-gd/file-scope.d.ts.map +1 -1
  13. package/dist/converter/ts-to-gd/file-scope.js +1 -1
  14. package/dist/converter/ts-to-gd/file-scope.js.map +1 -1
  15. package/dist/converter/ts-to-gd/gd-getset.js +2 -2
  16. package/dist/converter/ts-to-gd/gd-getset.js.map +1 -1
  17. package/dist/converter/ts-to-gd/index.d.ts.map +1 -1
  18. package/dist/converter/ts-to-gd/index.js +1 -0
  19. package/dist/converter/ts-to-gd/index.js.map +1 -1
  20. package/dist/converter/ts-to-gd/parameters.d.ts.map +1 -1
  21. package/dist/converter/ts-to-gd/parameters.js +2 -2
  22. package/dist/converter/ts-to-gd/parameters.js.map +1 -1
  23. package/dist/converter/ts-to-gd/statements.d.ts.map +1 -1
  24. package/dist/converter/ts-to-gd/statements.js +1 -1
  25. package/dist/converter/ts-to-gd/statements.js.map +1 -1
  26. package/dist/typings/override-system.d.ts.map +1 -1
  27. package/dist/typings/override-system.js +6 -0
  28. package/dist/typings/override-system.js.map +1 -1
  29. package/package.json +6 -1
  30. package/typings/classes/AnimationNodeBlendSpace1D.d.ts +2 -17
  31. package/typings/classes/AnimationNodeBlendSpace2D.d.ts +2 -17
  32. package/typings/classes/Area3D.d.ts +7 -14
  33. package/typings/classes/ArrayMesh.d.ts +0 -15
  34. package/typings/classes/AudioEffectAmplify.d.ts +2 -2
  35. package/typings/classes/AudioEffectBandLimitFilter.d.ts +1 -1
  36. package/typings/classes/AudioEffectBandPassFilter.d.ts +1 -1
  37. package/typings/classes/AudioEffectCapture.d.ts +9 -10
  38. package/typings/classes/AudioEffectChorus.d.ts +28 -57
  39. package/typings/classes/AudioEffectCompressor.d.ts +9 -11
  40. package/typings/classes/AudioEffectDelay.d.ts +15 -21
  41. package/typings/classes/AudioEffectDistortion.d.ts +10 -19
  42. package/typings/classes/AudioEffectEQ.d.ts +2 -2
  43. package/typings/classes/AudioEffectEQ10.d.ts +2 -2
  44. package/typings/classes/AudioEffectEQ21.d.ts +2 -2
  45. package/typings/classes/AudioEffectEQ6.d.ts +2 -2
  46. package/typings/classes/AudioEffectFilter.d.ts +9 -25
  47. package/typings/classes/AudioEffectHardLimiter.d.ts +5 -8
  48. package/typings/classes/AudioEffectHighPassFilter.d.ts +1 -1
  49. package/typings/classes/AudioEffectHighShelfFilter.d.ts +1 -1
  50. package/typings/classes/AudioEffectLimiter.d.ts +4 -9
  51. package/typings/classes/AudioEffectLowPassFilter.d.ts +1 -1
  52. package/typings/classes/AudioEffectLowShelfFilter.d.ts +1 -1
  53. package/typings/classes/AudioEffectNotchFilter.d.ts +1 -1
  54. package/typings/classes/AudioEffectPanner.d.ts +2 -7
  55. package/typings/classes/AudioEffectPhaser.d.ts +7 -9
  56. package/typings/classes/AudioEffectPitchShift.d.ts +3 -3
  57. package/typings/classes/AudioEffectReverb.d.ts +8 -17
  58. package/typings/classes/AudioEffectSpectrumAnalyzer.d.ts +5 -4
  59. package/typings/classes/AudioEffectStereoEnhance.d.ts +4 -7
  60. package/typings/classes/AudioServer.d.ts +2 -2
  61. package/typings/classes/AudioStreamPlaybackResampled.d.ts +0 -8
  62. package/typings/classes/BaseButton.d.ts +1 -1
  63. package/typings/classes/Basis.d.ts +0 -4
  64. package/typings/classes/Camera3D.d.ts +1 -1
  65. package/typings/classes/CameraAttributes.d.ts +0 -1
  66. package/typings/classes/CameraFeed.d.ts +0 -4
  67. package/typings/classes/CanvasItem.d.ts +1 -1
  68. package/typings/classes/CodeEdit.d.ts +0 -4
  69. package/typings/classes/CollisionObject2D.d.ts +0 -6
  70. package/typings/classes/CollisionPolygon2D.d.ts +0 -5
  71. package/typings/classes/CollisionShape2D.d.ts +0 -5
  72. package/typings/classes/Container.d.ts +0 -6
  73. package/typings/classes/Control.d.ts +0 -59
  74. package/typings/classes/DPITexture.d.ts +0 -14
  75. package/typings/classes/Decal.d.ts +0 -1
  76. package/typings/classes/Dictionary.d.ts +0 -1
  77. package/typings/classes/DisplayServer.d.ts +1 -128
  78. package/typings/classes/EditorDock.d.ts +2 -8
  79. package/typings/classes/EditorExportPlugin.d.ts +0 -5
  80. package/typings/classes/EditorFileSystem.d.ts +0 -2
  81. package/typings/classes/EditorInterface.d.ts +2 -4
  82. package/typings/classes/EditorSettings.d.ts +133 -197
  83. package/typings/classes/EditorSpinSlider.d.ts +0 -6
  84. package/typings/classes/Environment.d.ts +2 -7
  85. package/typings/classes/FileAccess.d.ts +1 -1
  86. package/typings/classes/FileDialog.d.ts +1 -1
  87. package/typings/classes/FoldableContainer.d.ts +1 -1
  88. package/typings/classes/Font.d.ts +2 -2
  89. package/typings/classes/GPUParticles3D.d.ts +0 -4
  90. package/typings/classes/GeometryInstance3D.d.ts +1 -1
  91. package/typings/classes/GradientTexture2D.d.ts +0 -2
  92. package/typings/classes/Image.d.ts +0 -1
  93. package/typings/classes/ImageTexture.d.ts +2 -0
  94. package/typings/classes/ImporterMesh.d.ts +0 -6
  95. package/typings/classes/Input.d.ts +13 -148
  96. package/typings/classes/InputEvent.d.ts +0 -8
  97. package/typings/classes/InputEventGesture.d.ts +3 -2
  98. package/typings/classes/InputEventMouse.d.ts +3 -2
  99. package/typings/classes/InputEventWithModifiers.d.ts +3 -2
  100. package/typings/classes/InputMap.d.ts +0 -3
  101. package/typings/classes/ItemList.d.ts +2 -9
  102. package/typings/classes/Joint3D.d.ts +0 -1
  103. package/typings/classes/LookAtModifier3D.d.ts +1 -2
  104. package/typings/classes/MainLoop.d.ts +0 -4
  105. package/typings/classes/NativeMenu.d.ts +3 -10
  106. package/typings/classes/Node.d.ts +5 -9
  107. package/typings/classes/OS.d.ts +2 -2
  108. package/typings/classes/Object.d.ts +2 -5
  109. package/typings/classes/OptionButton.d.ts +0 -6
  110. package/typings/classes/PCKPacker.d.ts +0 -4
  111. package/typings/classes/ParticleProcessMaterial.d.ts +0 -5
  112. package/typings/classes/PhysicsDirectBodyState2D.d.ts +1 -2
  113. package/typings/classes/PhysicsDirectBodyState3D.d.ts +1 -2
  114. package/typings/classes/PhysicsServer2D.d.ts +2 -2
  115. package/typings/classes/PhysicsServer2DExtension.d.ts +1 -1
  116. package/typings/classes/PhysicsServer3D.d.ts +42 -145
  117. package/typings/classes/PopupMenu.d.ts +8 -22
  118. package/typings/classes/PopupPanel.d.ts +0 -2
  119. package/typings/classes/PortableCompressedTexture2D.d.ts +2 -0
  120. package/typings/classes/ProjectSettings.d.ts +60 -44
  121. package/typings/classes/RDShaderSPIRV.d.ts +0 -30
  122. package/typings/classes/RDShaderSource.d.ts +0 -10
  123. package/typings/classes/RenderingDevice.d.ts +0 -104
  124. package/typings/classes/RenderingServer.d.ts +11 -213
  125. package/typings/classes/Resource.d.ts +2 -2
  126. package/typings/classes/ResourceImporterDynamicFont.d.ts +1 -3
  127. package/typings/classes/ResourceImporterSVG.d.ts +0 -10
  128. package/typings/classes/ResourceImporterTexture.d.ts +2 -2
  129. package/typings/classes/SceneTree.d.ts +29 -5
  130. package/typings/classes/ScriptEditor.d.ts +0 -6
  131. package/typings/classes/Shader.d.ts +0 -2
  132. package/typings/classes/Shape3D.d.ts +0 -1
  133. package/typings/classes/SplitContainer.d.ts +0 -6
  134. package/typings/classes/String.d.ts +1 -1
  135. package/typings/classes/SubViewport.d.ts +0 -4
  136. package/typings/classes/TextEdit.d.ts +0 -3
  137. package/typings/classes/TextServer.d.ts +0 -2
  138. package/typings/classes/TextServerExtension.d.ts +0 -2
  139. package/typings/classes/Texture2D.d.ts +0 -14
  140. package/typings/classes/TextureRect.d.ts +1 -4
  141. package/typings/classes/Tree.d.ts +2 -9
  142. package/typings/classes/TreeItem.d.ts +0 -8
  143. package/typings/classes/Tween.d.ts +0 -13
  144. package/typings/classes/Viewport.d.ts +0 -10
  145. package/typings/classes/VisualShader.d.ts +0 -2
  146. package/typings/classes/VisualShaderNodeColorFunc.d.ts +2 -2
  147. package/typings/classes/VoxelGIData.d.ts +0 -10
  148. package/typings/classes/Window.d.ts +0 -23
  149. package/typings/classes/XRCamera3D.d.ts +1 -1
  150. package/typings/classes/XRServer.d.ts +0 -2
  151. package/typings/classes/_globals.d.ts +86 -45
  152. package/typings/classes/index.d.ts +0 -5
  153. package/typings/godot-class-registry.json +108 -1254
  154. package/typings/classes/AccessibilityServer.d.ts +0 -405
  155. package/typings/classes/AwaitTweener.d.ts +0 -10
  156. package/typings/classes/BlitMaterial.d.ts +0 -22
  157. package/typings/classes/DrawableTexture2D.d.ts +0 -47
  158. 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
  ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/convert.webp)
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
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_scene.png)
40
+
41
+ You get type hints for this:
42
+
43
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_hint_1.png)
44
+
45
+ …or this:
46
+
47
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_hint_2.png)
48
+
49
+ …or even this:
50
+
51
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_hint_3.png)
52
+
53
+ `preload()` is typed too:
54
+
55
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_preload.png)
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
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_gdscript_error.png)
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
+ ![](https://raw.githubusercontent.com/nnn3d/typescript-to-gdscript/HEAD/docs/assets/showcase_converter_error.png)
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` files into your output dir (default `scripts/`).
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
- Use `tstogd watch` during development for auto-conversion plus live diagnostics.
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 init # interactive scaffold (tsconfig + tstogd.json + ts-plugin)
63
- tstogd convert # convert TS GD (cached, with diagnostic check)
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
- Cheat sheet with all basics to write scripts, with comments showing the generated GDScript. For advanced helpers and deep dive, see the [**full cheat sheet** in docs/transform-rules.md](docs/transform-rules.md#full-cheat-sheet).
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
- // ── Inner classes, class-level constants & enums (namespace merging) ──
98
- // A `namespace Player { ... }` block paired with the same-named class
99
- // lifts each `export`ed member into the class as a nested member.
100
- // Put constants and enums INSIDE the namespace — that's how GD
101
- // class-level `const` and `enum` are declared.
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; // → const MAX_HP = 100 (on Player)
159
+ export const MAX_HP = 100;
104
160
 
105
161
  export enum State {
106
162
  IDLE,
107
163
  RUNNING,
108
- } // → enum State { ... } (on Player)
164
+ }
109
165
 
110
166
  export class Bullet extends Node2D {
111
- // class Bullet extends Node2D:
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
- // ── Statics (mutable) for constants, use the namespace block above ──
118
- static MAX_INSTANCES = 8; // → static var MAX_INSTANCES = 8
119
- // `readonly` is a TS-only contract — emits a plain `var` on GD.
120
-
121
- // ── Field decorators ─────────────────────────────────────────────
122
- // Every Godot annotation works as a decorator.
123
- // `@exports` is the only special alias:
124
- // `@export` is a TS reserved word, so the plural form emits
125
- @exports speed: float = 200.0; // → @export var speed: float = 200.0
126
- @export_range(0, 100) health: int = 100; // → @export_range(0, 100) var health: int = 100
127
- @onready sprite: Sprite2D; // @onready var sprite: Sprite2D
128
-
129
- // ── Signals — named tuple labels become GD arg names ─────────────
130
- health_changed = gd.signal<[from: int, to: int]>(); // → signal health_changed(from: int, to: int)
131
- died = gd.signal(); // signal died
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
- // → func _init(speed: float = 200.0):
136
- this.speed = speed; // self.speed = speed
188
+ this.speed = speed;
137
189
  }
138
190
 
139
- // ── Getters / setters native TS accessors ──────────────────────
191
+ // Getters / setters (native TS accessors)
140
192
  get score(): int {
141
193
  return this.score;
142
- } // → var score: int:
194
+ }
143
195
  set score(v: int) {
144
196
  this.score = v;
145
- } // get: return score
146
- // set(value): score = value
197
+ }
147
198
 
148
- // ── Async / await Promise<T> unwraps to T on the GD side ───────
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
- // → func _process(delta: float):
157
- // ── Operators ──────────────────────────────────────────────────
158
- let eq = this.health === 0; // → var eq = self.health == 0
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
- // ── Math on value types (Vector2, Color, …) needs gd.ops ───────
162
- let pos = gd.ops.add(this.position, Vector2(1, 0)); // → var pos = (self.position + Vector2(1, 0))
163
- // Available: add, sub, mul, div, rem, eq, ne, gt, gte, lt, lte, plus (unary), minus (unary)
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
- // ── Type casting / checking ────────────────────────────────────
166
- let body = gd.as(this.get_node('Body'), CharacterBody2D); // → var body = self.get_node("Body") as CharacterBody2D
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
- /* class check */
169
- } // → if body is CharacterBody2D:
218
+ /* body is CharacterBody2D */
219
+ }
170
220
  if (gd.is(this.health, int)) {
171
- /* primitive */
172
- } // → if self.health is int:
221
+ /* health is int */
222
+ }
173
223
 
174
- // ── Object construction ────────────────────────────────────────
175
- let bullet = new Player.Bullet(); // → var bullet = Bullet.new()
224
+ // Object construction instead of Player.Bullet.new()
225
+ let bullet = new Player.Bullet();
176
226
 
177
- // ── Dictionaries (string keys — for non-string keys use gd.dict) ──
178
- let stats = { name: 'Hero', hp: 100 }; // → var stats = { "name": "Hero", "hp": 100 }
227
+ // Dictionaries (other keys: gd.dict)
228
+ let stats = { name: 'Hero', hp: 100 };
179
229
 
180
- // ── Strings, StringName, NodePath ──────────────────────────────
181
- let greet = `Hi ${this.name}!`; // → var greet = "Hi " + str(self.name) + "!"
182
- let path = NodePath('Body/Sprite'); // → var path = ^"Body/Sprite"
183
- let sig = StringName('died'); // → var sig = &"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
- // ── Control flow ───────────────────────────────────────────────
186
- for (let s of range(3)) print(s); // → for s in range(3): print(s)
235
+ // Control flow
236
+ for (let s of range(3)) print(s);
187
237
 
188
- // ── Match — native TS switch round-trips with GD `match` ───────
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
- #### Useful global types
218
-
219
- Each interface is a map from a string literal (a `res://` path or a group name) to the typed entity that path resolves to. You index them directly to get the typed value:
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
- | Interface | Keys | Values | Example |
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
- The Godot-typed `load()`, `preload()`, `get_node()`, and autoload singletons all index these interfaces under the hood.
354
+ #### Useful global types
231
355
 
232
- #### The `*Name` key-union aliases
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
- Each base interface has a parallel alias defined as `keyof <Interface>`, so you can constrain a function argument to **only paths/names that exist in the project**:
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
- | Alias | Equivalent to | Typical use |
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
- `keyof` is 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:
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'); // ❌ TS2345
385
+ this.preload_asset('res://does_not_exist.png'); // ❌ error
260
386
  this.count_in_group('enemies'); // ✅ typed
261
- this.count_in_group('typo'); // ❌ TS2345
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":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/converter/common/index.ts"],"names":[],"mappings":"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"}
@@ -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
- // Strip generic args for class types
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) &&