Inconsistent Invalid index type for dictionary

Godot Version

4.6.1.stable

Question

Why is the Error below given when trying to use an Object reference as key?

When defining and declaring the dictionary before the init function the compiler accepts it and the application starts.
It also correctly fills the dictionary as seen in the second screenshot.

Error at (13, 24): Invalid index type “GDScriptNativeClass” for a base of type “Dictionary[Object, String]”.

"String" is of type String, but String is of type type (or something similar as Godot does not have extensive type meta programming)

Object is not an Object, just like String is not a String

3 Likes

That may be, but this still does not explain the behaviour.
And also the following does not produce any errors, which gives the impression that Object reference and insatance are both of type Object

var	object				:	Object	=	Object
var	object_instance		:	Object	=	Object.new()

func	_init():
	object	=	Object

It’s tricky because GDScripts can also act as a type, so some types can cast down to Objects. Nevertheless it’s strange behavior I wouldn’t rely on at this point, and it’s certainly not helping your use case. Does my first post help your problem? If not you should post your real code/problem.

This is a fair point, then i guess my problem boils down to:
How do i properly type the Dictionary?

I want to use the class reference as the key and the instance of the class as the value.

var	dict_object_object_instance	:	Dictionary[Object,	Object]	=	
{Object			:	Object.new()}

I don’t think you can do that, unless Godot adds a type type i.e Dictionary[type, Object]

why do you want to store types as keys and an instance as the value? This may be an XY Problem

I am using it as a caching system.
I create the object the first time it is requested and place it in a cache/loaded dictionary.
This way the object will stay in memory and the next time it is requested i can check if there is already an instance of this object available.

I use it in a base class for UI Control pages.
When there is a child page it can be cached in a modular way and i don’t need to change any code when creating a new page object.

You should be able to use Variant as a type type instead of Object. Your example then should look like that:

	var	dict_object_object_instance	:	Dictionary[Variant,	Variant]	=	\
	{Object			:	Object.new()}

and you can retrieve/check it as such

	dict_object_object_instance[Object]
1 Like

Caching might not be helping as much as you think, especially for UI. At a base level the operating system will already cache files read, so re-reading will be much faster. Godot will also cache loaded resources, guaranteeing a similar much faster re-loading. Adding objects to the scene tree will take resources, usually rendering and especially 3D rendering objects are the worst offenders. If you are trying to save from instancing and destroying the same UI panel, showing/hiding would be more performant but we’re talking about a minuscule operation that only happens on occasion, if it were per-frame then it might be worth looking into optimizations.

If you continue this route anyways, it sounds like you could use any type as the key; it may be better to use a string path to the scene or script you want to instantiate and keep.

To illustrate this a bit:

class Derived extends Node2D:
	pass
	

func _ready() -> void:
	var type_check := Derived.new()
	
	print("typeof: ", type_string(typeof(type_check)))
	print("get_class: ", type_check.get_class())
	
	print("is Derived: ", type_check is Derived)
	print("is Node2D: ", type_check is Node2D)
	
	print("== Derived: ", type_check == Derived)
	print("== Node2D: ", type_check == Node2D)
	
	print("is_class Derived: ", type_check.is_class("Derived"))
	print("is_class Node2D:", type_check.is_class("Node2D"))
typeof: Object
get_class: Node2D
is Derived: true
is Node2D: true
== Derived: false
== Node2D: false
is_class Derived: false
is_class Node2D:true

3 Likes

Super appreciate this; really good quick reference of the weirdness/boundary that GDScript classes walk

2 Likes

Doing these check with the Object reference as well as the Object instance gives interesting results.
Showing that Object reference is of type Object.

func	_init():
	var	object			:	Object	=	Object
	var object_instance :	Object	=	Object.new()

	print("typeof: ", type_string(typeof(object)))
	print("get_class: ", object.get_class())
	print("is Object: ", object is Object)
	print("== Object: ", object == Object)
	print("is_class Object: ", object.is_class("Object"))

	print("typeof: ", type_string(typeof(object_instance)))
	print("get_class: ", object_instance.get_class())
	print("is Object: ", object_instance is Object)
	print("== Object: ", object_instance == Object)
	print("is_class Object: ", object_instance.is_class("Object"))
typeof: Object
get_class: GDScriptNativeClass
is Object: true
== Object: true
is_class Object: true

typeof: Object
get_class: Object
is Object: true
== Object: false
is_class Object: true

Furthermore, the following code still seems inconsistent.

var	dict_object_object_instance	:	Dictionary[Object,	Object]	=	{}

func	_init():
	dict_object_object_instance[Object]	=	Object.new()    # Throws Error
	set_dict_object_object_instance(Object,	Object.new())   # Works fine

	var	object			:	Object	=	Object
	dict_object_object_instance[object]	=	Object.new()    # Works fine

func	set_dict_object_object_instance(a_key:	Object,	a_value:	Object):
	dict_object_object_instance[a_key]	=	a_value

The function and the dictionary are both typed the same, yet only the line directly using ‘Object’ as the key throws an error