Fixing a `UNSAFE_CAST` warning when duck typing

I have a question regarding duck typing and the strict warnings like UNSAFE_CAST:

Say I have this function in GDScript:

func _on_body_entered(body: Node2D) -> void:
    if "health" in body:
        (body.health as Health).take_damage(damage)
        queue_free()

The (body.health as Health) cast is seen as an UNSAFE_CAST and produces the following warning in the debugger:

W 0:00:00:0214   The value is cast to "Health" but has an unknown type.
  <GDScript Error>UNSAFE_CAST
  <GDScript Source>projectile.gd:51

How would I restructure my code to properly ‘solve’ this warning? It seems impossible with duck typing?

An alternative to the cast could be to use the has_method and call functions:

if body.health.has_method("take_damage"):
  body.health.call("take_damage", damage)

Unfortunately, that will just result in four (!) other warnings:

The property "health" is not present on the inferred type "Node2D" (but may be present on a subtype).
  <GDScript Error>UNSAFE_PROPERTY_ACCESS

The method "has_method()" is not present on the inferred type "Variant" (but may be present on a subtype).
  <GDScript Error>UNSAFE_METHOD_ACCESS

The property "health" is not present on the inferred type "Node2D" (but may be present on a subtype).
  <GDScript Error>UNSAFE_PROPERTY_ACCESS

The method "call()" is not present on the inferred type "Variant" (but may be present on a subtype).
  <GDScript Error>UNSAFE_METHOD_ACCESS

If it’s really impossible to ‘fix’ these warnings, why are they warnings in the first place? Seems rather strange to build a script language and give the user they can’t do anything about…

1 Like

Thanks to @dalexeev in the Godot chat, I now know how to work around this! Here’s the solution he proposed:

func _on_body_entered(body: Node2D) -> void:
	var health: Health = body.get("health")
	if health != null:
		health.take_damage(damage)
		queue_free()

It takes a little bit more code, but it’s type safe and does not generate warnings! yay.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.