The place where random ideas get written down and lost in time.
2024-07-09 - Godot GDSCript Static Typing
Category DEVGDSCript: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/index.html
https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/static_typing.html
var damage = 10.5 ⇒ no typing, this is essentially a Variant
var damage:= 10.5 ⇒ inferred typing
var damage: float = 10.5 ⇒ explicit typing
For constants:
const MOVE_SPEED = 50.0 ⇒ inferred (= and := are the same)
const MOVE_SPEED := 50.0 ⇒ inferred
const MOVE_SPEED: float = 50.0 ⇒ explicit
For functions:
func sum(a: float = 0.0, b: float = 0.0) -> float: …
func sum(a := 0.0, b := 0.0) -> float:
Which types to use:
Types can be:
- Variant, void (only function return, see below)
- Built-in types, global classes, inner classes (gdscript has inner classes?!)
- Native classes, enums (gdscript has enums?!)
- Constants that are preloaded as class types (see below)
func blah() -> Variant: … ⇒ forces the function to return something
func blah() -> void: … ⇒ forces the function to not return a value
A note about class_name:
- When used, it “registers” the class name globally, and it can be used anywhere.
- Without it, one can “locally” preload a gd into a constant to create a class name:
const Rifle = preload("res://player/weapons/rifle.gd")
var rifle: Rifle
load(“some.gd”) happens at runtime and can go only into a “var”.
preload(“some.gd”) happens at compile time (reference) and goes into a “const”.
Array types:
The syntax is “ var: Array[type]”:
var scores: Array[int] = [10, 20, 30]
var vehicles: Array[Node] = [$Car, $Plane]
var items: Array[Item] = [Item.new()]
Used for for loops, but ignored in array methods and operators (array + array, or array[index]).
In Godot 4.2, an untyped array can be locally typed in a for loop:
for name: String in names: …
Typed dictionaries are currently not supported.
Type casting and checking:
Example:
func _on_body_entered(body: PhysicsBody2D) -> void:
var player := body as PlayerController # ⇐ null if it cannot cast
if not player:
return
The type casting keyword is “as” : “instance as type”.
Warning ⇒ Use := to force the var to be typed -- apparently, it would not be overwise.
The destination var would be null if the type cannot be cast.
Warning ⇒ The silent null in case of non cast is allegedly a frequent source of bugs.
A safer way is thus to do an “is” check (like I was doing, because I didn’t know better anyway):
if not (body is Type): … skip or pass
var player: Type = body # ⇐ this throws if it cannot cast
etc
(I typically just write “if not body is Type” but I notice the example wraps that in parentheses??)
Inner Classes
Not related to type hinting, just types in general, GDScript does have inner classes:
class_name SomeClass
…
class SomeInnerClass:
var a = 5
func someFunc(): …
func _init():
var c = SomeInnerClass.new()
Question: are these static inner classes or are they bound to their parent class?
Class Constructor vs Static Constructor
An init constructor can call a parent base class:
class_name SomeClass
func _init(arg):
super(“some value”, arg)
and have a static initializer:
static var my_static_var = 1
static func _static_init():
my_static_var = 2
Typed Getters and Setters:
Example:
var seconds: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
Note the “: Type:” syntax.
If the getter/setters have functions, there’s an alternative syntax:
var my_prop:
get = get_my_prop, set = set_my_prop
var my_prop: get = get_my_prop, set = set_my_prop
Lambda Functions:
Lambda functions are created inline as such:
var myLambda1 = func (param1, …paramN):
body
var myLambda2 = func(): print(“something”)
or directly as arguments:
someobject.connect( func(): print(“blah”) )
The type of a lambda is a Callable https://docs.godotengine.org/en/stable/classes/class_callable.html#callable
Note that a Callable is also a type that can call any function by name:
func something(arg1, arg2) : …
var myCallable = Callable(self, “something”).
myCallable.call(arg1, arg2)...