I’ve been scouring the documentation and search results and surprisingly just cannot find an answer to this:
I have a Variant value. I know it should contain an int value. How do I get that value in a way that doesn’t produce warnings or errors with GDScript’s type system? A type guard like if value is int: [do stuff with value] doesn’t work, I still get an error when trying to use value as an int. A cast like value as int doesn’t work, this produces its own error, nor does int(value).
What’s the correct way to cast or coerce a Variant to a primitive type?
Here’s an example showing the general form of the code I’m working with and want to make work with types.
I’ve tried any number of things but I cannot seem to get the Variant values from the dictionary to be accepted as typed function arguments without type system errors. For example The argument 1 of the function "do_stuff_with_items_data()" requires the subtype "int" but the supertype "Variant" was provided. as-is, or The value is cast to "int" but has an unknown type. when trying value as int, or The argument 1 of the constructor "int()" requires the subtype "int", "bool", or "float" but the supertype "Variant" was provided. when trying int(value). And similar errors for the strings.
The solution is to define a Resource instead of this JSON-like data form. Also, you’ll be able to attain disk-saving advantages with Resource, such as the ResourceSaver.save() utility, and directly load it with the load() function.
I am aware that this is an option. But there are two reasons why I would still appreciate an answer to the original question:
This isn’t my code. It involves code from an addon. I could just fully rewrite it, but I would prefer not to.
I very much doubt that this will be the last time I run into a situation, especially trying to work with others’ GDScript code, where I need to know how to work with Variants without just turning off the type errors that are extremely helpful otherwise.
I admittedly did not test on 4.2 since I don’t have that version downloaded, but I copied your code into both 4.1 and 4.3beta3, and it worked fine for both of them.
Ahh, that explains why i didn’t get them!
Now that I can replicate it, I think you’re out of luck. It seems that those settings treat casting a var to an int (or to anything else I would assume) as unsafe, and since Dictionary cannot be statically typed, you’re stuck with either not using static typing and treating everything as a var, or dealing with seeing/hiding at least one of the errors.
Admittedly I’m not well versed in the deep ins and outs of Godot, so there may be an answer there for you that I don’t know, but I think the general problem is that GDScript relies very heavily on vars and vars aren’t particularly safe to cast.
It really seems like there should be a way in GDScript to check the type of a Variant’s value and retrieve it. I know this is possible with the C# bindings. Is there really just no way to do it in GDScript, short of turning off type safety?
Bizarrely, I have been able to fix the type errors by casting first to Variant and then to the primitive type. (And using an explicit type with the for loop variable.)
Like so:
extends Node
enum ItemsEnum {
ITEM_ONE,
ITEM_TWO,
}
const items_data: Dictionary = {
ItemsEnum.ITEM_ONE: {
"value": 1,
"title": "Title one",
"tooltip": "Tooltip one",
},
ItemsEnum.ITEM_TWO: {
"value": 2,
"title": "Title two",
"tooltip": "Tooltip two",
},
}
func _ready() -> void:
for id: int in items_data:
self.do_stuff_with_items_data(
id,
(items_data[id].value as Variant) as int,
(items_data[id].title as Variant) as String,
(items_data[id].tooltip as Variant) as String,
)
func do_stuff_with_items_data(id: int, value: int, title: String, tooltip: String) -> void:
pass # etc
It’s not helpful but I think the problem comes from casting non-typed variables.
If the variable is actually typed as ‘Variant’ then casting it doesn’t give an error. Something like this:
var test_int: Variant = 1
var test_str1: Variant = "One"
var test_str2: Variant = "Two"
func _ready() -> void:
for id: int in items_data:
self.do_stuff_with_items_data(
id,
test_int as int,
test_str1 as String,
test_str2 as String,
)
func do_stuff_with_items_data(id: int, value: int, title: String, tooltip: String) -> void:
pass # etc
Actually, nevermind. Declaring the test variables in my code as not-typed works too, no cast warning. But the values from the dictionary are seen as “unknown type”. I don’t know why either…