Caveats
Your TypeScript becomes GDScript, so it can only do what GDScript can. Some TypeScript syntax has no GDScript counterpart, and some behaves differently once converted. This page lists both.
The converter reports everything under “Unsupported syntax” as an error, in your editor as you type and from tstogd convert, so none of it slips into a .gd unnoticed. The differences further down convert fine, so they are worth knowing before they surprise you.
Unsupported syntax
Section titled “Unsupported syntax”- Destructuring, such as
let [a, b] = pairorlet { x } = point. GDScript has none. Assign each value on its own line. for...in. It walks keys in TypeScript, while GDScript’sfor x inwalks values. Usefor...of, anddict.keys()for a dictionary’s keys.x in array. It checks for an index in TypeScript, while GDScript’sinchecks for an element. Usearray.has(x).inon a dictionary checks for a key in both and is fine.??and??=. GDScript has no such operator. Use a ternary:x !== null ? x : fallback.- Optional chaining
?.. GDScript has no short-circuiting member access. Check fornullfirst; a dictionary read already givesnullfor a missing key. - Spread,
f(...args)and[...list]. A call can’t take a variable number of arguments in GDScript. Pass the values one by one, and join arrays withgd.ops.add(a, b). A rest parameter in a declaration (f(...args: int[])) is fine. - Top-level
letandconst. GDScript has no variables outside a class. Put constants in the class’s namespace and variables in the class. - More than one class per file. A
.gdfile is one class. Move the others to files of their own, or make them inner classes. - A class without
extends. GDScript would pickRefCountedfor you. Write the base class, even when it isRefCounted. - String enums,
enum E { A = 'a' }. GDScript enum values are integers. Use integer values, or string constants. undefined. GDScript has onlynull. Writenull, and write an optional parameter asx: int | null = nullrather thanx?: int.var. This one is a warning: it converts to GDScriptvar, but TypeScript’svaris scoped to the function. Uselet, which matches GDScript’svar, orconst.- Operators GDScript lacks:
>>>,&&=,||=, the comma operator,void, andx++used as a value. Rewrite with plain statements;x++;on its own line is fine. - Labels on loops, with
break labelorcontinue label. GDScript has no labels. Use a flag or an earlyreturn. - Parameter properties,
constructor(public x: int). Declarexas a field and assign it in the constructor. - Default and namespace imports,
import Foo fromandimport * as ns from. GDScript has nothing like them. Import classes by name:import { Foo } from './foo'.
Differences to know
Section titled “Differences to know”&& and || return a bool
Section titled “&& and || return a bool”In TypeScript a || b gives one of the operands. GDScript’s or and and always give true or false. Using one as a value is an error. Pick a value with a ternary (a ? a : b), or wrap the expression in bool() when a boolean is what you want. Conditions (if (a && b)) are unaffected.
switch becomes match
Section titled “switch becomes match”match branches never fall through, so write each case without break. A break that leaves the switch is an error. Empty cases stacked above another share its body, as in TypeScript. default becomes _ and always goes last. See switch on an enum.
== and === are the same
Section titled “== and === are the same”Both become GDScript ==. It compares vectors, colors, arrays and dictionaries by their contents, not by identity: two separate arrays [1, 2] are equal. Use is_same(a, b) to check whether two values are the same object.
Integer division
Section titled “Integer division”/ between two int values drops the fraction (7 / 2 is 3), and TypeScript can’t warn you. See Math and value types.
Dictionary reads return null
Section titled “Dictionary reads return null”A plain object is a Dictionary in GDScript. Reading obj.key becomes obj.get("key"), which gives null for a missing key, never undefined.
Value types are copied
Section titled “Value types are copied”Vector2, Color, Transform2D and the other value types are copied when you assign them, even though TypeScript sees them as objects. let p = this.position; p.x = 1; doesn’t move the node. Arrays and dictionaries are shared, as in TypeScript.
A constructor runs the parent’s only through super()
Section titled “A constructor runs the parent’s only through super()”A constructor becomes _init, and GDScript runs the parent class’s _init from it only when you call super(). TypeScript lets you leave super() out here, and then the parent’s constructor is skipped. A class with no constructor of its own still gets the parent’s.
Lambdas copy the local variables they use
Section titled “Lambdas copy the local variables they use”count += 1 inside an arrow function doesn’t change the outer count in Godot: a GDScript lambda gets its own copy of the local variables it uses, and TypeScript doesn’t warn you. Keep shared state in a field (this.count). See Functions and lambdas.
Coroutines, not promises
Section titled “Coroutines, not promises”An async method is a GDScript coroutine. await is the only way to get its result: .then, .catch, .finally, new Promise and keeping an unawaited result are all errors. See Coroutines.
Inherited properties can’t be redeclared
Section titled “Inherited properties can’t be redeclared”TypeScript lets a subclass declare a field its base already has, such as name: string on a Node. GDScript refuses such a script. Rename the field.
Details
Section titled “Details”Restrictions in the reference has the full list, with the reason for each item.
