Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. GDScript
  3. GDScript Types and Static Typing Practice
GDScript0 exercises

GDScript Types and Static Typing Practice

Master GDScript gradual typing in Godot 4: type hints, typed arrays, typed dictionaries (4.4+), Variant, type casting with as/is, and the performance gains from static typing.

Common ErrorsQuick Reference
On this page
  1. 1What types does GDScript have?
  2. 2How do you add type hints in GDScript?
  3. 3What are typed arrays and typed dictionaries?
  4. 4How does type casting work?
  5. 5Does static typing make GDScript faster?
  6. 6What is Variant in GDScript?
  7. 7How do enums work in GDScript?
What types does GDScript have?How do you add type hints in GDScript?What are typed arrays and typed dictionaries?How does type casting work?Does static typing make GDScript faster?What is Variant in GDScript?How do enums work in GDScript?

GDScript is gradually typed. You can mix dynamic and static code freely in the same file, and Godot won't complain. But adding type hints catches bugs at parse time, unlocks editor autocomplete, and makes your code measurably faster.

This page covers the full type system: from basic : int annotations to typed arrays, typed dictionaries (new in 4.4), casting with as, and the Variant type that sits underneath everything. If you need a refresher on script structure first, see foundations. For typed array iteration patterns, see arrays & loops.

Related GDScript Topics
GDScript FoundationsGDScript Arrays & LoopsGDScript ExportsGDScript BestsGDScript Signals

GDScript has 38+ built-in types. Here are the ones you'll use most in 2D games:

CategoryTypes
Primitivesbool, int, float
StringsString, StringName, NodePath
VectorsVector2, Vector2i, Vector3, Vector3i
EngineColor, Callable, Signal
ContainersArray, Dictionary
Packed arraysPackedByteArray, PackedVector2Array, etc. (10 types)

One thing that trips people up: most built-in types are pass-by-value. Vector2, Color, int, float all get copied on assignment or when passed to a function. Modifying a Vector2 inside a function does not change the original.

The exceptions are Object (and all subclasses like Node, Resource), Array, Dictionary, and packed arrays. These are pass-by-reference.

Ready to practice?

Start practicing GDScript Types and Static Typing with spaced repetition

Three syntaxes cover everything:

# Explicit type
var health: int = 100
var player_name: String = "Hero"
var enemies: Array[Node2D] = []

# Type inference with :=
var speed := 50        # Inferred as int
var damage := 10.5     # Inferred as float
var label := "Hello"   # Inferred as String

# Function signatures
func take_damage(amount: float) -> bool:
	return health <= 0

For-loop variables can be typed too:

for name: String in names:
	print(name)

for i: int in range(10):
	print(i)

Constants work with both : and :=. There's no difference for constants since the value is known at compile time: const MAX_SPEED: float = 200.0 and const MAX_SPEED := 200.0 are identical.

Typed arrays landed in Godot 4.0. Add the element type in brackets:

var scores: Array[int] = [10, 20, 30]
var enemies: Array[Node] = [$Goblin, $Skeleton]
var items: Array[Item] = [Item.new()]

Godot checks the type on every write operation at runtime. Try to insert a wrong type and you get a runtime error. This also works with @export, giving you a smarter Inspector widget.

Typed dictionaries arrived in Godot 4.4 (March 2025). Same bracket syntax:

var fruit_costs: Dictionary[String, int] = {"apple": 5, "orange": 10}
var positions: Dictionary[Vector2i, Item] = {Vector2i(0, 0): Item.new()}

One limitation to know: nested typed collections are not supported. Array[Array[int]] won't compile. You can only go one level deep: Array[Array] works, but the inner array is untyped.

GDScript has two type operators: as for casting and is for checking.

Casting with as behaves differently depending on the type category:

# Object types: returns null on failure (safe)
var player := body as PlayerController
if not player:
	return  # Cast failed, body is not a PlayerController

# Built-in types: throws an ERROR on failure (dangerous!)
var num := value as int  # Crashes if value is a String

This asymmetry is a common source of bugs. For Object types, as is a safe "try-cast." For built-in types like int or String, a failed cast crashes your game.

Checking with is is always safe:

if body is PlayerController:
	body.damage()  # Editor knows body is PlayerController here

if value is int:
	print("It's an integer")

A useful pattern for node references:

@onready var timer := $Timer as Timer  # Green line in editor

One gotcha: is type narrowing is not always recognized by the editor. After if event is InputEventMouseMotion:, you might still see UNSAFE_PROPERTY_ACCESS warnings when accessing event.relative. The workaround is to assign to a typed variable inside the block.

Yes. Typed GDScript generates optimized opcodes that bypass the Variant dispatch system. Instead of unwrapping a Variant, checking its type, looking up the operator, performing the operation, and wrapping the result, typed code takes a direct shortcut.

Independent benchmarks (1 billion iterations, M2 Max) show concrete gains:

OperationSpeedup with types
Integer arithmetic28-36% faster
Vector2 distance calculation55-59% faster
Native function calls (fully typed)Up to 150% faster

The biggest wins come from vector math and engine API calls. If your movement code does position += velocity * delta every frame, typing those variables costs nothing and gives you a measurable speedup.

Variant is Godot's universal container type. It occupies 24 bytes on 64-bit platforms and can hold any of the 38+ built-in types. Every untyped variable in GDScript is a Variant.

var x = 5       # x is a Variant holding an int
var y: int = 5  # y is a native int, no Variant wrapper

Variant matters because many engine methods return it. Array.pop_back(), Dictionary.get(), signal parameters, and dynamic function arguments are all Variant. To get type safety back, you need to cast explicitly:

var text: String = arr.pop_back()  # Implicit Variant -> String
var node := arr.pop_back() as Node  # Explicit cast

One important nuance: only Object-typed variables can be null. Built-in value types (int, float, Vector2, etc.) always hold a valid value. There is no "null int" in GDScript.

Enums are named groups of integer constants:

enum State { IDLE, JUMP = 5, SHOOT }
# State.IDLE = 0, State.JUMP = 5, State.SHOOT = 6

Values auto-increment from the previous entry. You can use enums as type hints:

var current_state: State = State.IDLE

But enums are int under the hood. var x: State = 999 compiles without complaint, even if 999 is not a valid state. GDScript does not validate enum ranges at compile time. See exports tuning for exposing enums to the Inspector.

What You'll Practice: GDScript Types and Static Typing

Declaring typed variables with `: Type` and `:=` inferenceUsing typed arrays and typed dictionariesType casting with `as` and type checking with `is`Writing fully-typed function signaturesUnderstanding Variant and when to cast explicitly

Common GDScript Types and Static Typing Pitfalls

  • `as` on built-in types (int, float, String) throws a runtime error instead of returning null, unlike Object types where it safely returns null
  • Enums are int under the hood, so `var x: MyEnum = 999` compiles even if 999 is not a valid enum value
  • Nested typed collections are not supported: `Array[Array[int]]` and `Dictionary[String, Dictionary[String, int]]` won't compile
  • Reading @export values in `_init()` returns script defaults, not Inspector values. Read them in `_ready()` instead
  • All Object-typed variables are nullable with no way to express non-null in the type system

GDScript Types and Static Typing FAQ

Is GDScript statically typed or dynamically typed?

GDScript is gradually typed. You can write fully dynamic code with no type annotations, fully static code with types everywhere, or any mix. The compiler enforces types where you add them and treats everything else as Variant.

Should I use type hints in GDScript?

Yes. Type hints catch errors at parse time instead of runtime, enable editor autocomplete, and generate faster bytecode. The cost is a few extra characters per declaration. The benefit is fewer bugs, better tooling, and measurable performance gains (28-59% depending on the operation).

What is the difference between String and StringName in GDScript?

String is a mutable sequence of Unicode characters. StringName is an immutable, interned string where only one instance of each unique value exists in memory. StringName comparisons are pointer-based (instant), making them faster for dictionary keys, signal names, and method lookups. Godot uses StringName internally for node names and methods.

Do typed arrays work with custom classes in GDScript?

Yes. Array[MyClass] works for any class registered with class_name. Godot checks the type at runtime on every write operation. If you try to insert an object of the wrong type, you get a runtime error.

When were typed dictionaries added to GDScript?

Godot 4.4, released March 2025. The syntax is Dictionary[KeyType, ValueType]. Like typed arrays, type checking happens at runtime on write operations.

What does := mean in GDScript?

:= is the type inference operator. var x := 5 infers the type as int from the right-hand side. It gives you the same type safety as var x: int = 5 with less typing. For constants, := and explicit typing are identical since the value is always known at compile time.

How much faster is typed GDScript?

Benchmarks show 28-36% faster integer arithmetic, 55-59% faster vector operations, and up to 150% faster native function calls when fully typed. The gains come from bypassing the Variant dispatch system with type-specific opcodes.

Can GDScript variables be null?

Only Object-typed variables (Node, Resource, and their subclasses) can be null. Built-in value types like int, float, Vector2, and String always hold a valid value. There is no "null int" or "null Vector2" in GDScript.

What is the Variant type in Godot?

Variant is Godot's universal container type. It is 24 bytes on 64-bit platforms and can hold any built-in type. Every untyped GDScript variable is a Variant. Many engine methods return Variant, which means you need explicit casts to get type safety back.

How do enums work in GDScript?

Enums define named integer constants: enum State { IDLE, RUN, JUMP }. Values auto-increment from 0. You can use them as type hints (var state: State), but GDScript does not validate enum ranges at compile time. An enum variable happily accepts any integer, even one outside the defined values.

GDScript Types and Static Typing Syntax Quick Reference

Typed variable declarations
var health: int = 100
var speed := 50
var enemies: Array[Node2D] = []
var direction: Vector2 = Vector2.ZERO
Typed function signature
func take_damage(amount: float) -> bool:
	health -= int(amount)
	return health <= 0
Typed arrays and dictionaries
var scores: Array[int] = [10, 20, 30]
var loot: Dictionary[String, int] = {"gold": 50, "gems": 3}
Type casting and checking
var player := body as PlayerController
if body is PlayerController:
	body.take_hit(damage)
Enum declaration
enum State { IDLE, RUN, JUMP }
var current: State = State.IDLE

Further Reading

  • GDScript Dictionary map() and map_in_place12 min read
  • Facade Pattern in Godot 4 GDScript: Taming "End Turn" Spaghetti12 min read

Start practicing GDScript Types and Static Typing

Free daily exercises with spaced repetition. No credit card required.

← Back to GDScript Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.