godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.11k stars 69 forks source link

Add static type hints for dictionary members #56

Open ghost opened 4 years ago

ghost commented 4 years ago

Describe the project you are working on: Cutscene-heavy RPG

Describe how this feature / enhancement will help your project: I want to associate some object with some identifier to make it easy to reference them from the AnimationPlayer (in this case it's some Dialogue class with data for who is talking and what they're saying.) I would like to access this data using the format read_dialogue(id: String), where the id is the key in a dictionary that leads to the data. This causes my extended AnimationPlayer to pause the animation so it can autotype some text and wait for player input before moving on with the animation.

However, there's currently no way to define a dictionary to have any particular kinds of keys and values, so if, for example, I wanted to add 50 different lines of text to read in this animation, I'd have to set the key's type and the value's type manually every time, which is tedious.

image

Furthermore: without dictionary hints, it's not possible to add a hint to a particular kind of Object, since the list of types you can pick from is limited to built-ins and various primitive types. Contrast this to arrays, where you can use hints to specify just about any type not present in the drop-down. So, you're not going to be able to easily create resources in-place, resulting in the OmniResource issue (except worse since it's every object ever.)

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:

export(Dictionary, String, Resource) var hinted_dictionary

64645509-ab31a700-d3da-11e9-8d57-f0ca866362b2

If this enhancement will not be used often, can it be worked around with a few lines of script?: I think just about everyone will be very pleased with this addition; however, in the meantime, it's not too bothersome to avoid the issue. Instead I'll give my class an id: String property and loop through an array. I assume this isn't as efficient as using dictionaries, but I likely won't notice a real performance hitch. Nonetheless, it's a shame exported dictionaries can't be more useful.

https://github.com/godotengine/godot/issues/25157

RichardEllicott commented 3 years ago

this would be really nice, yes at the moment for example i really wanted to add a dictionary such that i can add other types not on the list, for example:

<String, AudioStream> for a keyed list of sounds i can load from the inspector

we have it already with lists

SoDaRa commented 3 years ago

This would be a very nice feature. I'm doing a VN and would like to replicate the functionality of SpriteFrames in Animated Sprite to help pack all the images for a character into a scene to make it easier to compartmentalize. But using SpriteFrames causes all the images in it to be loaded, even if they aren't used. Which is understandable, but if we packed over 100 images into a character, suddenly it's going to eat up a lot of video memory and likely won't play nice with mobile. All I really need is the path to each image so I can load them when they're needed. So a Dictionary would be a perfect way to replicate the SpriteFrames resource when calling on the sprite with parameters of which sprite resources need to be loaded. But setting up the key and value type for hundreds of images is a chore. So this would help a lot.

cgbeutler commented 3 years ago

While I don't know/care what the short-hand export syntax would look like It seems like typing exports for dictionaries and arrays may be better as a more "deep" style. I think this has been mentioned elsewhere, but I couldn't find it. The 'property info' dictionaries are used all over, like for function parameters and return values. The 'property info' syntax could be used here as well to hold sub-types. This may actually reduce complexity in some places.

In the context of _get_property_list it may look something like:

func _get_property_list() -> Array:
  return [
    { name = "my_dict", type = TYPE_DICTIONARY,
      hint_types = [
        { type = TYPE_STRING }, #key property
        { type = TYPE_INT, hint = PROPERTY_HINT_RANGE, hint_string = "0,10" }, #value property
      ]
    }
  ]

The above somewhat mimics the "Generics" list in some languages, which may also play well when hooking c# in. I imagine the name field could be added for subtypes, if that seems like its easier. It is usually ignored for function return types and a few other spots.

The editor itself could then do a more recursive-ish rendering of inspectors when it comes to Dictionaries or Arrays. There would be no real depth-limit to the underlying representation. The short-hand export may still have a depth limit, but there would be a long-hand way to get around it, if needed.

As array stands in 3.3, export(Array, Array, float) var arr := [] will give you the property info {class_name:, hint:24, hint_string:19:3:, name:arr, type:19, usage:8199}. The hint string '19:3:' appears to just be a concat of type enums, which kinda cuts out any options or custom resources.

chexmo commented 2 years ago

I came here from https://github.com/godotengine/godot/issues/25157 since someone said that that issue continues here. I want to know if there are any news related to exporting dictionaries... As you can see in the image below, in godot 3.4.3 it still doesn't support typed dicts image

Calinou commented 2 years ago

I want to know if there are any news related to exporting dictionaries...

This feature isn't planned for 4.0, as we don't intend to add new features to GDScript to ensure it can stabilize for 4.0.

sairam4123 commented 2 years ago

Is this feature planned for at least 4.x? I'd love to see this feature in 4.1 or 4.2. Some kind of approval from code developers could be good to hear!

YuriSizov commented 2 years ago

Is this feature planned for at least 4.x? I'd love to see this feature in 4.1 or 4.2.

Some kind of approval from code developers could be good to hear!

I think it's fair to say that it is desired by people who use static typing inside and outside of the core team. But we'll get to it when we get to it. Unless @vnen wants to pinky promise?.. ๐Ÿ™ƒ

Frontrider commented 2 years ago

Godot 4 has typed dictionaries (I think) and arrays, so I doubt that import hints are required anymore.

Calinou commented 2 years ago

Godot 4 has typed dictionaries and arrays, so I doubt that import hints are required anymore.

Godot 4.0 only has typed arrays, not typed dictionaries.

bitbrain commented 1 year ago

It is not explicitly stated here but a syntax like this would be great (and consistent with the Array syntax too)

@export var dictionary : Dictionary[String, Node] = {}
aalhitennf commented 1 year ago

It is not explicitly stated here but a syntax like this would be great (and consistent with the Array syntax too)

@export var dictionary : Dictionary[String, Node] = {}

But what happens with nested dictionarys? This syntax works only if dictionary is used like hashmap, allowing only one type as value. Lets say you parse json (that can have various types, nested dictionaries of various types etc) to dictionary, that would require typescript-like interfacing or something, otherwise it seems a bit pointless and it would make more sense to introduce completely new type (hashmap, map or whatever), instead of allowing typing for just this one use case.

bitbrain commented 1 year ago

But what happens with nested dictionaries?

If you have to nest dictionaries, perhaps introducing new types might be a good alternative. This makes things way more readable and easier to test. Compare this (pseudo-code)

var dictionary: Dictionary[Dictionary[Dictionary[String, int], int], int] = {}

to something like this instead:

class MyKeyType:
   # content here

var dictionary: Dictionary[MyKeyType, int] = {}
Frontrider commented 1 year ago

Yeah, nested dictionaries are a whole another beast, but I'd expect them to work the same. I guess we see a reason why some languages opt for "<>" to mark typing, especially if you use square brackets for arrays. My eyes do start looking for the array in that.

aalhitennf commented 1 year ago
class MyKeyType:
   # content here

var dictionary: Dictionary[MyKeyType, int] = {}

This works but adds little overhead in cases where the data is updated fairly often since you need to turn those dictionaries into class objects (or update them) every time.

PoisonousGame commented 1 year ago

@export var dictionary : Dictionary{String:Node} = {}

@export var dictionary : Dictionary<TKey,TValue> = {}

@export var dictionary : Dictionary[String, Node] = {}

bitbrain commented 1 year ago

@export var dictionary : Dictionary{String:Node} = {}

@export var dictionary : Dictionary<TKey,TValue> = {}

@PoisonousGame those would not be consistent with the Array syntax:

@export var array : Array[String] = []

unless we say that we should change that syntax too (breaking change -> would break dozens of already migrated projects that use Godot 4)

Frontrider commented 1 year ago

@export var dictionary : Dictionary[String, Node] = {}

IF 4 gets it then it should be this.

Rubrickus commented 1 year ago

I hope this proposal gets some love. I'm fairly new to Godot, and adding entries to dictionaries in the editor is pretty painful, due to the giant types menu (which you have to select from twice for every new entry).

As a (possibly much simpler) partial workaround at the editor level, what about a modification of the "Add Key/Value Pair" button (or maybe a contextual menu?) for "Add Key/Value Pair With Same Types as Previous Entry? (I'll admit I don't know how to convey that succinctly in text!). I think this would greatly improve the most common painful use case without needing any change to the parser etc. (I realize this is a distinct proposal; I'll probably file it as such next.)

iamjoeysox commented 1 year ago

Would love to see this myself.

Calinou commented 1 year ago

@iamjoeysox Please don't bump issues without contributing significant new information. Use the :+1: reaction button on the first post instead.

TrickyFatCat commented 1 year ago

But what happens with nested dictionaries?

If you have to nest dictionaries, perhaps introducing new types might be a good alternative. This makes things way more readable and easier to test. Compare this (pseudo-code)

var dictionary: Dictionary[Dictionary[Dictionary[String, int], int], int] = {}

to something like this instead:

class MyKeyType:
   # content here

var dictionary: Dictionary[MyKeyType, int] = {}

Alternatively, Maps can be introduced to the engine. Unreal Engine has maps and they work exactly like the proposed statically typed dictionaries. In this case, everyone will be happy, in my opinion.

var my_map : Map[String, Node] = {}
var my_dic : = {"One": 1, "Two": 2}
Calinou commented 1 year ago

Alternatively, Maps can be introduced to the engine. Unreal Engine has maps and they work exactly like the proposed statically typed dictionaries. In this case, everyone will be happy, in my opinion.

The "map" term is just a synonym of "dictionary" (there's even "associative array" in PHP speak). I don't see why we'd need to add another type to support this.

TrickyFatCat commented 1 year ago

The "map" term is just a synonym of "dictionary" (there's even "associative array" in PHP speak). I don't see why we'd need to add another type to support this.

Agree, I just suggested possible solution. Personally, I'm happy with typed dictionaries suggested prevously.

@export var my_typed_dictionary : Dictionary[Node, int]

caioreix commented 1 year ago

Another suggestion is use like golang maps.

@export var my_typed_dictionary : Dictionary[String]int = {
"key": value
}
Sawrr commented 1 year ago

TypeScript has "interfaces" to define custom types for objects of known structure, including nested objects https://www.typescriptlang.org/docs/handbook/2/objects.html

A GDScript equivalent would be helpful to provide type checking and editor hints.

For a Dictionary with this structure:

var user = {
    "name": "Sawr",
    "id": 123,
    "roles": [
        "user",
        "moderator"
    ],
    "data": {
        "avatar": {
            "url": "avatar.png",
            "size": 64
        }
    }
}

a GDScript-equivalent of a TypeScript "interface" might look like:

interface User {
    name: String,
    id: int,
    roles: Array[String],
    data: {
        avatar: {
            url: String,
            size: int
        }
    }
}

Currently, the editor's auto suggestion shows the properties and methods of Dictionary only. If using a custom-typed Dictionary, the suggestions would include the names of the keys:

i.e. in the above example, if the developer types user. the auto suggestion would show name, id, roles, and data in addition to the built-in suggestions for Dictionary. To me this makes sense as user.name is valid syntax for accessing Dictionary keys.

Frontrider commented 1 year ago

@Sawrr that topic is #6416 and #5641.

Sawrr commented 1 year ago

@Sawrr that topic is #6416 and #5641.

Thanks for linking those proposals, however they seem to be proposing functionality for Node.

My comment is specific to the Dictionary variant type, for when you just want to use a JSON-like object for data only and don't need the extra functionality of a Node

A Dictionary-specific custom type might be used with a similar syntax to typed arrays i.e. var user: Dictionary[User]

And if defining them with the keyword interface is too general, we might use some dictionary-specific keyword instead like

dict User {
    name: String,
    ....

GDScript already supports initializing a Dictionary with a JSON-like syntax, so adding editor hints for the keys would speed up development. Currently there is no check that a key exists (or is spelled correctly)

Frontrider commented 1 year ago

GDScript already supports initializing a Dictionary with a JSON-like syntax, so adding editor hints for the keys would speed up development. Currently there is no check that a key exists (or is spelled correctly)

Thing is, if it went like other languages, then you'd deserialize a the json into a node/object. The way dictionary works in GDScript as of now is more like when you deserialize into a JSONObject type.

The Dictionary is not for this. The best we could have IMO is to serialize a Resource type into json. (and that might not be a bad idea at all)

A Map exists not to be an arbitrary data structure but because this kind of key-value pair is common enough to not to make a brand new type for each use-case. In every language it is specifically bad at doing that ("arbitrary structures"), and it is always complemented with the type system to fill in that "hole". It can not be anything more complex than that, else it becomes more difficult to use then it needs to be.

Sawrr commented 1 year ago

GDScript already supports initializing a Dictionary with a JSON-like syntax, so adding editor hints for the keys would speed up development. Currently there is no check that a key exists (or is spelled correctly)

Thing is, if it went like other languages, then you'd deserialize a the json into a node/object. The way dictionary works in GDScript as of now is more like when you deserialize into a JSONObject type.

The Dictionary is not for this. The best we could have IMO is to serialize a Resource type into json. (and that might not be a bad idea at all)

Not sure I understand -- my suggestion is not about serialization or deserialization.

Using dictionaries to hold data is very convenient, and often that data has a known structure. GDScript already gives us the ability to create and manage state using Dictionary, so my suggestion is to improve editor support by letting developers declare known structures for data.

A Map exists not to be an arbitrary data structure but because this kind of key-value pair is common enough to not to make a brand new type for each use-case. In every language it is specifically bad at doing that ("arbitrary structures"), and it is always complemented with the type system to fill in that "hole". It can not be anything more complex than that, else it becomes more difficult to use then it needs to be.

Sorry, but I don't understand this argument either. GDScript already supports initializing and accessing Dictionaries with JavaScript-like syntax. TypeScript interfaces are just an editor hint that makes coding faster and catching mistakes easier when accessing objects properties.

i.e. for pure state, Dictionary already supports the very convenient syntax that developers coming from the JavaScript world know and love, so it seems natural to consider adding support for types

Frontrider commented 1 year ago

Not sure I understand -- my suggestion is not about serialization or deserialization.

From javascript that is where I landed, as that is the usecase for gdscript. ๐Ÿ˜…

Sorry, but I don't understand this argument either. GDScript already supports initializing and accessing Dictionaries with JavaScript-like syntax. TypeScript interfaces are just an editor hint that makes coding faster and catching mistakes easier when accessing objects properties.

i.e. for pure state, Dictionary already supports the very convenient syntax that developers coming from the JavaScript world know and love, so it seems natural to consider adding support for types

There is no language in the world where a dictionary/map does what you are asking for, and for good reason. GDScript does NOT have javascripts dynamic typing that typescript tries to make into something usable. The GDScript equivalent of what you are asking for IS to use a resource/object/refcouted with a defined type, as that is exactly what is happening with typescript/javascript. We don't need a second type system and that is where this lands in.

But, I do think you are touching on something that could be very worth having, just not with a dictionary. Something like this instead (substitute Node3D for your custom type):

var y_position = 2
var node = Node3D{
    #set a field
    # the value of "self" becomes this new Node3D, if we are using a field that is not on self then it looks for the "context" above it.
    transform = Transform{
        origin = Vector3{
           y = y_position
        }
   }
}

More or less taken from here: https://kotlinlang.org/docs/type-safe-builders.html

Some people could argue that this is basically the same thing as updating each field one by one, line by line but I personally beg to differ. Every time I see this it becomes significantly easier to read (less verbose) than that. This is most likely the closest thing I can think of to what you were asking, without having a second type system.

But that is a different topic, the dictionary can not have this.

dalexeev commented 1 year ago

Let's separate the proposal for typed dictionaries (typed keys and values, for example Dictionary[String,int]) and the proposal for typed dictionary "members" (struct). Just like typed arrays (for example Array[int]) are not the same as typed tuples (essentially the same struct, but its fields are unnamed, just indexes).

In theory, we could add an "ephemeral" type for dictionaries (the terms "metatype" and "pseudotype" are already taken), as in the case of enums, which are just an illusion over int. But it seems to me that this is a bad idea, since dictionaries are a composite type, unlike int. An "ephemeral" type gives almost no guarantees, the dictionary can still be changed at runtime (including by engine or other languages) so that it no longer matches the declared type. The purpose of static types is not only convenient autocompletion in the editor, but also certain guarantees.

Personally, I'd rather see typed dictionaries (i.e. typed keys and values) rather than as "structures", since the two kinds of dictionary typings are likely to be very difficult and confusing to implement and use. Structs are also useful, but perhaps we should implement them differently. Also now instead of structures you can use Resource, RefCounted, or Object.

See also:

Sawrr commented 1 year ago

To clarify, types in TypeScript are not "real" types at all, they are just editor hints -- it's still just JavaScript at runtime. And yet, adding those "types" which give no runtime guarantees still makes coding much more pleasant. In the same way, I'm not proposing any changes to Dictionary itself, but looking for a way to "suggest" a structure for the data to improve the developer experience.

The problem with using RefCounted or Object is that, for the use case of needing a data container only, it's nowhere near as convenient as using Dictionary's JS-like syntax to create and access:

As Frontrider mentioned, instantiating a class with .new() and setting properties line-by-line gets verbose. With nested properties, you can imagine having to define multiple classes and call .new() at each level of nesting, just for the editor to suggest and check what properties your data has.

That said, I can see how supporting a "type" system specific to Dictionary could seem unpalatable or confusing when most people are thinking in terms of GDScript's static typing.

AnidemDex commented 1 year ago

While this catch some attention, I thought about a plugin to do this (the dictionary part about editor inspector), where it's format is simply โ€œ:" for hint string. It uses those two to know what values should be in new keys, and that's it

muchitto commented 1 year ago

I think both Dictionary[String, int] static typing and (more significantly) describing the structure of the data in the dictionary would be beneficial.

I think one of the powers of Typescript for instance is the ability to easily and quickly create objects (without heavy classes) and then describe the structure of the object so that the code can be checked and is more robust.

Usually I just want to whip up some simple structures for some part of the game logic and use them just for some structured data. It would be super awesome if there is a way for me to say that yes, these are the fields that I'm going to use and I'm not going to create new ones dynamically so check that I'm not typoing or anything stupid like that.

Something like this (just like in Typescript):

var test : { "test": String } = {
    "test": "yes"
}

Also why not give the ability to predefine the type easily the same way as in Typescript:

type TestType = {
   "test": String
}

And of course, it would be awesome if the structure could be somehow inferred from the assigned value also.

Just my thought.

Frontrider commented 1 year ago

I think both Dictionary[String, int] static typing and (more significantly) describing the structure of the data in the dictionary would be beneficial.

As stated, the dictionary is meant to be used as a Map type, storing key value pairs. That is what it is for. You are pointing at a hole, but it may not be a task for the dictionary, we may want to have some kind of struct. (NOT the same thing as a map!)

Also why not give the ability to predefine the type easily the same way as in Typescript:

type TestType = {
   "test": String
}

And of course, it would be awesome if the structure could be somehow inferred from the assigned value also.

Just my thought.

Typescript just uses javascripts (otherwise terrible) dynamic type system, which really should not be a thing for GDScript.

muchitto commented 1 year ago

I think both Dictionary[String, int] static typing and (more significantly) describing the structure of the data in the dictionary would be beneficial.

As stated, the dictionary is meant to be used as a Map type, storing key value pairs. That is what it is for. You are pointing at a hole, but it may not be a task for the dictionary, we may want to have some kind of struct. (NOT the same thing as a map!)

Also why not give the ability to predefine the type easily the same way as in Typescript:

type TestType = {
   "test": String
}

And of course, it would be awesome if the structure could be somehow inferred from the assigned value also. Just my thought.

Typescript just uses javascripts (otherwise terrible) dynamic type system, which really should not be a thing for GDScript.

I'm mostly interested in the ergonomics of creating a typed structured data without the need for actual bulky classes. If they are a separate construct from dictionaries, I do not care as long as the ergonomics of creating the typed data is as trivial as creating a dictionary currently.

But note that for instance JSON data is converted to a dictionary (and in Typescript/Javascript it is converted to an object, which is, let's face it, used both as a lightweight structured data as well as a dictionary most of the time) so if we would like to cover those cases, typing dictionary structures would be beneficial.

So I think there is merit in how Typescript has handled this case even though people like to shit on Javascript/Typescript.

GammaGames commented 1 year ago

This sounds like you're trying to recreate resources...

Frontrider commented 1 year ago

This sounds like you're trying to recreate resources...

You are 100% correct, that is the godot way, but it has the same "issue" as say java beans have with a very verbose syntax. That syntax is why people default to dictionaries instead, as it is significantly easier to read, and your data does not get lost in the code you need to write (actual problem in larger languages).

That is the reason why this keeps coming up. Which is not an issue with Dictionaries at the end.

But note that for instance JSON data is converted to a dictionary (and in Typescript/Javascript it is converted to an object, which is, let's face it, used both as a lightweight structured data as well as a dictionary most of the time) so if we would like to cover those cases, typing dictionary structures would be beneficial.

To be completely fair, dictionary/map is used in every single typed language for cases when you do not give the json parser a type (or object that behaves as if it was a map, and called something like JsonObject).

Godot's json parser is the issue for that, not the dictionary type.

I'm mostly interested in the ergonomics of creating a typed structured data without the need for actual bulky classes. If they are a separate construct from dictionaries, I do not care as long as the ergonomics of creating the typed data is as trivial as creating a dictionary currently.

This is correct, just not a problem with the dictionary. Open a separate issue for it and link it.

Sawrr commented 1 year ago

That is the reason why this keeps coming up. Which is not an issue with Dictionaries at the end.

Revisiting this thread, I agree. For devs with a background in Typescript, it's very tempting to use Dictionary as a data container because of the convenient initialization syntax. That's how I ended up here, and likely others too ๐Ÿ˜… apologies for derailing the original proposal

This is correct, just not a problem with the dictionary. Open a separate issue for it and link it.

I agree with this as well. I think we're looking for an initialization syntax that works for all classes -- that would remove the temptation to use Dictionary as something more than a Map. Your previously mentioned suggestion that is similar to the type-safe builders could be a great starting point for a new proposal

Frontrider commented 1 year ago

That's how I ended up here, and likely others too ๐Ÿ˜… apologies for derailing the original proposal

Revealed a real hole imo. That is more important.

SquidgySapphic commented 1 year ago

I am 110% for the addition of this whenever it's possible; if I had the knowledge of Godot's inner workings to implement it myself I'd love to take a crack at it, but alas am not there just yetโ€ฆ ๐Ÿ˜”

That aside; would having a separate class of object (such as a Map[key_type] and/or Map[key_type, value_type] or similarly labeled) make more sense than tacking this functionality onto Dictionary?

I'm only wondering this because as much as I agree, this would augment the Dictionary type VERY positively - have now visited this thread at least three times hoping for the good news ๐Ÿ˜‚ - it might make more sense to purposefully differentiate between the two for the sake of explicitly requiring type safety in certain cases, plus potentially packing data properly similarly to PackedThingyArray types, vs. having to (on the programmer side) account for mangled/mistyped data; not sure if I've worded this correctly, but hopefully my meaning isn't lost!

Hypothetically;

@export var strict_map : Map[int, String] = {
  42 : "Life",
  69 : "Hilarious",
  999 : 911  # This line throws a full-on error
}
@export_dictionary_hinted(int) var loose_dict : Dictionary = {
  123 : 456,
  6 : "I like this number"
}
@export_dictionary_hinted(int, String) var another_dict : Dictionary = {
  33 : "All the Threes",
  88 : "Two Fun Ladies",
  "error": "You fool, you've doomed us all!"  # This line drops a warning?
}

In this scenario you can easily read that the data must be specifically typed for the Map, but you're only going to get mildly scolded by the engine when you have incorrectly-typed data within your Dictionary, if you want the flexibility for some reason.

This might be totally unnecessary, extending the Dictionary syntax could well be a fine enough solution, though there could be complications that are side-stepped by just creating a separate dedicated class of object. The editor and inspector could, visually, treat them exactly the same but under the hood they could serve specifically distinct purposes. Anyone have thoughts on this? ๐Ÿค”

Calinou commented 1 year ago

it might make more sense to purposefully differentiate between the two for the sake of explicitly requiring type safety in certain cases

We don't expose separate type for typed Arrays โ€“ย packed Arrays are an entirely different concept; they predate typed Arrays from 4.x. I don't see why we should do that for Dictionary.

SquidgySapphic commented 1 year ago

We don't expose separate type for typed Arrays โ€“ packed Arrays are an entirely different concept; they predate typed Arrays from 4.x. I don't see why we should do that for Dictionary.

Sure, packed Arrays are a different concept; I had the initial thought of "it might be easier to add an export hint so the inspector can have faux typed Dictionaries as a temporary measure" and went from there.

It may be flawed logic - open to criticism here of course - but if it would be more time and work to jam in proper static typing for the existing Dictionary, it may be simpler to just keep Dictionary loose by existing definition and have a different type that gets added in a future update; I was figuring that for the sake of backward compatibility of code, that would be a fair compromise.

Mind you I agree even then, it wouldn't be too hard to go through type-hinted Dictionary code finding... @export_dictionary_typed(Foo, Bar) var foobar : Dictionary ...and fixing it up after the proper static typing is added to the, as-proposed... @export var foobar : Dictionary[Foo, Bar] ...instead - both of those would likely look the same in the inspector anyway, but with the latter being truly typed.

TL;DR fair point, makes sense, was jumping the gun re: avoiding deprecating code between minor engine versions.

Frontrider commented 1 year ago

Sure, packed Arrays are a different concept; I had the initial thought of "it might be easier to add an export hint so the inspector can have faux typed Dictionaries as a temporary measure" and went from there.

The inspector is only one part of the problem, it needs to be typed in gdscript as well. Both of those are real issues.

I'd even argue that the gdscript side is more important.

SquidgySapphic commented 1 year ago

I'd even argue that the gdscript side is more important.

Absolutely, that's not in question at all. It would be a lot better to see this in 4.1* than it would any further-down-the-line releases, but again, if it'd require an amount of work that delays other features for the next release, it might be better than not having this feature at all for now; though maybe not, given responses so far! ๐Ÿ˜…

*Edit: Meant 4.2, my bad!

Calinou commented 1 year ago

It would be a lot better to see this in 4.1 than it would any further-down-the-line releases

4.1 is in feature freeze, so any new feature development is slated for 4.2 at the earliest.

AnidemDex commented 1 year ago

What about dictionaries of dictionaries? Sure, <key_hint>: [<value_hint, ...] is a very temptative option, but what if the hint for values are dictionaries? How do you define that?

bitbrain commented 1 year ago

What about dictionaries of dictionaries?

@AnidemDex I gave an example to this here: https://github.com/godotengine/godot-proposals/issues/56#issuecomment-1288212196

IntangibleMatter commented 1 year ago

TypeScript has "interfaces" to define custom types for objects of known structure, including nested objects https://www.typescriptlang.org/docs/handbook/2/objects.html

A GDScript equivalent would be helpful to provide type checking and editor hints.

For a Dictionary with this structure:

var user = {
    "name": "Sawr",
    "id": 123,
    "roles": [
        "user",
        "moderator"
    ],
    "data": {
        "avatar": {
            "url": "avatar.png",
            "size": 64
        }
    }
}

a GDScript-equivalent of a TypeScript "interface" might look like:

interface User {
    name: String,
    id: int,
    roles: Array[String],
    data: {
        avatar: {
            url: String,
            size: int
        }
    }
}

Currently, the editor's auto suggestion shows the properties and methods of Dictionary only. If using a custom-typed Dictionary, the suggestions would include the names of the keys:

i.e. in the above example, if the developer types user. the auto suggestion would show name, id, roles, and data in addition to the built-in suggestions for Dictionary. To me this makes sense as user.name is valid syntax for accessing Dictionary keys.

May be solved by #7329. Offers very similar functionality.

Frontrider commented 1 year ago

TypeScript has "interfaces" to define custom types for objects of known structure, including nested objects https://www.typescriptlang.org/docs/handbook/2/objects.html

Always remember that typescript only tries to emulate the features of strictly typed languages on top of javascript however it can. It helps, but there is ALWAYS a more proper solution to whatever that language is doing.

May be solved by https://github.com/godotengine/godot-proposals/issues/7329. Offers very similar functionality.

That is exactly one of the things that we may want here instead of maps/dictionaries.