Enumerations or Enums are a staple of programming and are incredibly useful when developers would like to use integers, floats, or some other data type, by name from a set of values. Currently, there is no way to create custom enums in roblox, and although they can be mimicked by using tables, this prevents the Enum from receiving syntax notes from Luau.
In this example, roblox Enums are powerful for type specification, as they can be chosen distinctly by their parent class.
--- Extract physical properties from a material
-- If a {basePart} is provided, will report its own physical properties
local function GetFriction(material: Enum.Material, basePart: BasePart?): PhysicalProperties
-- TODO: body
end
And it would be useful to have a similar case for custom enumerations:
With "Enum"s generated from Lua class / table factories, Luau will be incapable of providing those type annotations and warnings, and you cannot build a type dynamically to represent the Enum without doing something annoying and crazy like this (and even then I cannot get GetEnumItems to work)
It would be great to have some sort of interface to create Enums dynamically.
The only other way to be able to type enums is to be able to manually declare sealed table types (i.e. type CustomEnum<T> = typeof(setmetatable(<CustomEnumItem<T, CustomEnum<T>>{}, metatable)))
It would also be incredibly helpful if they could be placed where regular Enums can be, like in object Attributes and serialized over event calls.
I like this idea as well. When I started developing on the platform, I was looking for a way to create enumerated types (C/C++ has the enum keyword). However, I have resorted to just using tables to do it like so:
-- ******** Requirements
-- Required Game Services and Facilities
local playerService = game:GetService("Players")
-- Scripting Support
local childWaitTime = 10
local serverScriptService = game:GetService("ServerScriptService")
local coreLibs = serverScriptService:WaitForChild("Libraries", childWaitTime)
assert(coreLibs, "Core library folder failed to load.")
-- Required Libraries
local enums = require(coreLibs.Enumerations)
It does work, and if you need to loop over the enumerations, you can do this:
for name, value in pairs(enums.NPCActionType) do
-- Do something with the enum.
print(name, "=", value)
end
But I digress. Being able to create custom Enums is something that is sorely needed, even if it’s just adding the enum keyword.
The preferred way to do this is using literal string unions.
type Color = "red" | "green" | "blue"
local function paintObject(color: Color)
-- etc
end
paintObject("red")
paintObject("orange") -- Type error
You can even combine these with other Luau tricks in order to exhaustive match.
if color == "red" then
-- red path
elseif color == "green" then
-- green path
else
-- This typecast will fail if any path isn't hit
local exhaustiveMatch: never = color
error(`Unknown color: {exhaustiveMatch}`)
end
Although this is the approach I have been using as well, it leads to problems like discoverability and typing. There is also a larger use for enums, for example, with flags and states
And enums being their own class / type makes it easier to discover and explicitly define enum types without having to use string literals. And there may be other cases where you would like to be able to distinguish from a string literal and an enum in a function, i.e. if using overloaded or variadic functions, or be able to iterate over enums and have the functionality of functions/properties like GetEnumItems(), enum.EnumType, enum.Name, and enum.Value.
While it is definitely possible to make a lua object to represent or copy the behavior of Enums and Flags, the typing system does not provide a way to be able to explicitly merge the keys of an initial table, so unless the “Enum” is a generic sealed table, typing will not provide any hints to the properties or objects of the enum.
CustomEnum.XY... -- will not perform type completion unless CustomEnum is an explicit sealed table
CustomEnum.XYZ.EnumType.X... -- ditto
for _, item in CustomEnum:GetEnumItems() do
print(item.EnumType.X... -- ditto
end
if myEnum:IsA(CustomEnum) then --[[...]] end
There are also problems when refactoring, as it can be more complicated to refactor a string that could be reused for other purposes, vs an enum
For discoverability, the autocomplete will automatically suggest the variants on a string union.
I understand your other use cases of things like GetEnumItems() equivalents but I think it’s unlikely we add much more than this as a first class feature.
Literal string unions don’t really do what enums do though
If you’re working with anything that doesn’t take strings (like APIs) then it’s not useful. Also IMO using strings for input is just kinda ugly.
I would really prefer to have a table of names and numbers (which is all an enum really is) than a union of a bunch of strings and while I can do that with just a normal table, I’d like proper integration with the checker and autocomplete
Please reconsider