Why can I not export an enum with a class_name?

Godot Version

4.6.2

Question

I have the following two scripts.

class_name Type
enum {
    ONE,
    TWO,
}

And:

class_name Stuff extends Resource

@export var type: Type

This throws the following error:

Error at (3, 1): Export type can only be built-in, a resource, a node, or an enum.

Why? If I put the enum inside the Stuff script, it works, but I want it to be global. I know I can instance some global autoload and access it as Global.Type, but that adds another layer of useless typing. The class_name makes the enum available everywhere, except the export does not like it. Is there a reason for this? And is there any workaround, or am I forever stuck with Global.Type.ONE instead of Type.ONE?

You can do it without strict typing.

class_name MyType

enum {
	ONE,
	TWO,
}
class_name Stuff extends Node

@export var my_type = MyType.ONE

But you cannot declare my_type as MyType. You can declare it as an int, which is all Enums are under the hood:

class_name Stuff extends Node

@export var my_type: int = MyType.ONE

Ultimately, you are trying to do something that Godot doesn’t really have support for. The next problem you’re going to run into is you can’t select your type from a dropdown in the inspector.

image

It’s even worse if you don’t type it as an int:

But once you embrace it, and do something like:

class_name MyType

enum Type {
	ONE,
	TWO,
}
class_name Stuff extends Node

@export var my_type: MyType.Type = MyType.Type.ONE

It doesn’t look so bad in the inspector:

Plus you can then do custom tooltips to make it even easier to read in the Inspector.

class_name MyType


enum Type {
	## My first type.
	ONE,
	## The second type I made.
	TWO,
}

Alternately, if you REALLY want short names, you can do something like this:

class_name MyType

const ONE = 1
const TWO = 2
class_name Stuff extends Node

@export var my_type: int = MyType.ONE

But you’re back to a poorer display in the Inspector:

Finally, you can use C#, GDExtension with C++ or roll your own version of the Godot engine and add your Enum in.


So there’s lots of workarounds. I don’t recommend using any. I understand why you want to, but after a while you get used to the way GDScript and Godot do things and you’ll appreciate the benefits.

Thank you for the explanation! It just seems very odd that Godot does not support this, but it is good to know that I did not just miss something somewhere.

The worst IMO is that this problem is theoretically solved by copying and pasting all of your enums at the beginning of every file in your project, because then it suddenly lets you export them with strict typing. I think this fact encourages very bad coding practice …

If you stick around, we talk a lot about good coding practices here. A number of us have decades of experiences as professional software developers. And we have opinions. :slight_smile:

I have adopted a practice I got from a Java developer I worked with years ago. He had a constants file in his project that he referenced. For some reason he also just called it K. (I think this may be because he was German and the word is spelled “konstante” in German?) He told me that he puts all his constants in one file so he always knows where to look.

I mostly do this. That’s because in Godot, a constant can be an object and you use preload() to load the file. If you put those constants in a central file, they would all be loaded whether they were needed or not.

I also used to be obsessed with short variable names. But I learned to program in the 80s when the length of your variable names affected the performance of your compiled code. I am now a big proponent of descriptive names. The only abbreviations I use are typically: i, x, y, z, min, and max. So even though it’s long, I use GameConstants. All my Enums and Constants go into a file.

My GameConstants.AnimationName Enum appears like this in the inspector:

image

And while GameConstants.AnimationName.IDLE is actually selected in that screen shot, it’s very human-readable. And when I’m in my code it is very clear what I am looking at. But the only place that code shows up is in the export definition:

@export var animation: GameConstants.AnimationName = GameConstants.AnimationName.IDLE_BLINKING

And the AnimatedSprite2D, AnimationPlayer, or AnimationTree that matches case on it (depending on how I’m animating a particular game).

Yes, I have now done the same. I just sometimes am not sure whether there might be a not-so-well-documented way of actually achieving my goal, so better to ask the pros!