"Type" type hint

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By linkdude240

Is there a way to specify that a type is an argument in a function?

Let’s say I want to get all children of a type (I do), either derived or the specific type.

Here’s what I’m doing (which works!) If I write the following code, I am able to get nested descendant objects matching the type I specify. Adding the type hint “Type” does not work as “Type” is not a valid type for the type hint:

NodeUtilities.gd

# Returns all descendants in the node that inherits from the provided type
# Note: to use this the calling script should preload a script into a constant
# and provide the constant as the type parameter
# Also if the descendant inherits the type it will still return the descendant
func get_descendants_by_type(node: Node, type) -> Array:
	
	var results:= []
	
	if node.get_child_count() > 0:
		
		for child in node.get_children():
				
			results.append_array(get_descendants_by_type(child, type))
			
			if child is type:
				
				results.append(child)
	
	return results

CharacterController.gd

extends KinematicBody2D

signal died

var health := 10

func _process(delta):
	
	if health <= 0:
		emit_signal("died")

func _ready():
    for effect in NodeUtilities.get_descendants_by_type(self, DeathEffect):
        self.connect("died", effect, "on_character_died")

DeathEffect.gd

extends AnimatedSprite
class_name DeathEffect
func on_character_died():
    play()

Hopefully in my reduction to a minimally reproducible example I didn’t misspell or omit anything.

My problem is that in NodeUtilities.get_descendants_by_type() I cannot properly provide a type hint to the type parameter.

Converted my answer to a comment since it wasn’t what you were looking for.

Use a string instead and the function get_class():

func get_children_of_type(node: Node, type: String) -> Array:
	var results:= []
    if node.get_child_count() > 0:
        for child in node.get_children():
            if child.get_class() == type:
                results.append(child)
	return results

LeslieS | 2023-02-09 05:43

This is not exactly what OP asked: The is operator will not check whether its the exact same class, but whether the given instance is assignable to that class.

MarcusRiemer | 2023-02-09 13:24

That is fair enough and accurate but in fact the poster does at least imply the exact class

children of a specific type (I do)   

He says nothing about assignable or inherits from etc.

LeslieS | 2023-02-09 17:15

Ah sorry, I did mean “of or inheriting from”, but this is still a great solution for finding classes of a particular type!

I was more wondering about the type hint.

Edited my original post for clarity instead of re-wording my question in a comment lol

linkdude240 | 2023-02-09 23:15

:bust_in_silhouette: Reply From: zhyrin

There are two ways to go about this:

  • You can mark your class as a global type. In the script file that defines Type, start the file with class_name Type, this will register it globally and Type will be accessible as a type in every script.
  • In the file where you want to use Type as a type, you can preload the script that defines this class like so: const Type := preload("res://path/to/type.gd"). Type will be available as a type in the scope of this file only.

Yes, I am able to do that when I want to say that an argument must be a specific type or inherited from a specific type.

What I am trying to do is restrict my parameter to accept only types. Not instances of a specific type, but type type objects themselves.

Edited my question to make that more clear instead of posting my clarifications in a comment

linkdude240 | 2023-02-10 16:12

I see, luckily for you I used something like this in a project, let me post a sample code :slight_smile:

zhyrin | 2023-02-10 18:09

:bust_in_silhouette: Reply From: zhyrin

I used something similar in the past but it doesn’t fit your needs exactly, I came up with a solution that should work:

func procedure(values: Array, path_to_desired_type: String) -> void:
    var DesiredType := load(path_to_desired_type)
    for value in values:
        if value is DesiredType:
            print("found value")

I see, so you are avoiding the “type” type by providing the path to the type instead. That is also a very interesting workaround! Although I am wondering what the return type of preload() and load() is.

linkdude240 | 2023-02-10 22:21

They load resources, so that’s the output type (of course this can be many different resource, in this case a script/gdscript type). In the meantime I found another solution, let me post that as well.

zhyrin | 2023-02-11 11:34

I think this answer works best for me, since you could probably do an export var and keep it flexible from a design standpoint, and while I will probably continue using the “real” type by typing in class_name MyClass and using that type directly, I still am kind of bummed that Godot does not have a built in type type that I can just use.

Thanks again for all the working examples and ideas!

linkdude240 | 2023-02-13 17:27

:bust_in_silhouette: Reply From: zhyrin

Here is another solution if you want to pass the type (objects script) as parameter, not the path to the script:

func procedure(values: Array, DesiredType: Script) -> void:
    for value in values:
        if is_of_type(value, DesiredType):
            print("found value")


func is_of_type(value, DesiredType: Script) -> bool:
    if not value is Object:
        return false
    return is_of_type_internal(DesiredType, (value as Object).get_script())


func is_of_type_internal(DesiredType: Script, CurrentType: Script) -> bool:
    return true if CurrentType == DesiredType else is_of_type_internal(DesiredType, CurrentType.get_base_script())