I have a scene and an attached script that adds a property called foo
I would have several instances of such scene and retrieve them as children of their container in such way:
var list:Array = container.get_children()
for object:Object in list:
if object.foo == something_something:
do_stuff()
Enabling all warnings in the debuggers tells me that
GDScript::reload: The property "foo" is not present on the inferred type "Object" (but may be present on a subtype).
<GDScript Error>UNSAFE_PROPERTY_ACCESS
Which makes sense of course, but I’m not sure how I could go about defining the retrieved children in such way so that the compiler won’t complain about this.
I know I could bombard my code with a plethora of
@warning_ignore(“unsafe_property_access”)
but it feels it defeats the point.
I’d like NOT to ignore those warning and to make sure I can differentiate between when I’m accessing a property that indeed exists VS dealing with a bug.
You must cast the type of the object. While resolving the warning you could still get a null runtime error if as cannot cast up.
var list:Array = container.get_children()
for object:Object in list:
var asFoobar := object as Foobar
if asFoobar.foo == something_something:
do_stuff()
Or you could assert that every object in list must be of the correct type for object: Foobar.
If you did not use a class_name for this object you can still declare the type through preloading their script
const Foobar = preload("res://foobar.gd")
# ...
for object: Foobar in list
If your script is built-in, or your object scripts are disparate yet share a var foo you could test if variable is within the object and either use get or again ignore the warning.
var list:Array = container.get_children()
for object:Object in list:
if "foo" in object and object.get("foo") == something_something:
do_stuff()
If you did not use a class_name for this object you can still declare the type through preloading their script
const Foobar = preload("res://foobar.gd")
# ...
for object: Foobar in list
This solved 90% of my cases, thanks!
The other 10% was something like:
func _input(event:InputEvent) -> void:
if event is InputEventKey and event.is_released():
if event.keycode == KEY_SPACE:
next_turn()
which I now have to rewrite as:
func _input(event:InputEvent) -> void:
if event is InputEventKey and event.is_released():
if "keycode" in event:
if event.get("keycode") == KEY_SPACE:
next_turn()
It feels a bit of a convoluted way of doing the same thing (having to use .get() instead of treating that as a normal property), but it seems to do the job.
I’m also surprised that I have to do so for built-ins, where I had assumed that “of course _input() is designed to handle InputEventKey elegantly”, but not a big deal.
This may be a better use of as since you are checking the type anyways with is
var event_key := event as InputEventKey
if event_key != null and event_key.is_released():
if event_key.keycode == KEY_SPACE:
next_turn()
In this case you are hoping for the type ShaderMaterial, not just Material.
But again if there are disparate yet shared methods, you can use has_method("foo") and call("foo", foo_arg1). This is the worst option though, just like "foo" in object and get this turns your compile-time warnings into runtime errors without any autocomplete, it only exacerbates the problem.