I don’t think is possible right now to do it dynamically. What I had to do was to build everything statically and essentially “trick” the intellisense with custom classes and generics. If you would like my Enumeration model that you can use/copy from, feel free to take it:
Enumerations.rbxm (2.1 KB)
It contains 3 ModuleScripts,
- Enumerations
--!strict
--[=[
Custom implementation of Roblox's Enums
@class Enumeration
]=]
local Enumeration = require(script.Enumeration)
local EnumerationItem = require(script.EnumerationItem)
local Enumerations = {}
-- Enums equivalent
export type Enumerations = typeof(Enumerations)
-- Enum equivalent
export type Enumeration = typeof(Enumeration.new("" :: string, {} :: {[string]: EnumerationItem}))
-- EnumItem equivalent
export type EnumerationItem = typeof(EnumerationItem.new("" :: string, 0 :: number, {} :: Enumeration))
-- Enumerations.Direction
export type Direction = typeof(Enumerations.Direction.Forward)
Enumerations.Direction = Enumeration.new("Directions", {
Forward = EnumerationItem.new("Forward", 1, Enumerations.Direction),
Backward = EnumerationItem.new("Backward", 2, Enumerations.Direction),
Left = EnumerationItem.new("Left", 3, Enumerations.Direction),
Right = EnumerationItem.new("Right", 4, Enumerations.Direction),
Up = EnumerationItem.new("Up", 5, Enumerations.Direction),
Down = EnumerationItem.new("Down", 6, Enumerations.Direction)
})
-- Enumerations.Action
export type Action = typeof(Enumerations.Action.Walk)
Enumerations.Action = Enumeration.new("Action", {
Walk = EnumerationItem.new("Walk", 1, Enumerations.Action),
Run = EnumerationItem.new("Run", 2, Enumerations.Action),
Dash = EnumerationItem.new("Dash", 3, Enumerations.Action),
Jump = EnumerationItem.new("Jump", 4, Enumerations.Action)
})
do
local self = setmetatable({}, {
__index = Enumerations,
__newindex = function(self: any, index: any): ()
error(`{index} cannot be assigned to`, 0)
end,
__tostring = function(): string
return script.Name
end,
}) :: any
return self :: typeof(Enumerations)
end
- Enumeration (inside Enumerations)
--!strict
local EnumerationItem = require(script.Parent.EnumerationItem)
local Enumeration = {}
export type Enumeration<T, N, V> = T & {
GetEnumItems: (self: Enumeration<T, N, V>) -> {EnumerationItem<T, N, V>}
}
export type EnumerationItem<T, N, V> = EnumerationItem.EnumerationItem<T, N, V>
function Enumeration.new<T, N, V>(name: string, enumerationItems: T): Enumeration<T, N, V>
local enumeration = {}
for enumerationItemName: string, enumerationItem: EnumerationItem<T, N, V> in enumerationItems :: any do
enumeration[enumerationItemName] = enumerationItem
end
function enumeration:GetEnumItems(): {EnumerationItem<T, N, V>}
local enumerationItemsToReturn: {EnumerationItem<T, N, V>} = {}
for _: string, enumerationItem: EnumerationItem<T, N, V> in enumeration :: any do
if typeof(enumerationItem) ~= "function" then
table.insert(enumerationItemsToReturn, enumerationItem)
end
end
return enumerationItemsToReturn
end
return setmetatable({}, {
__name = name,
__index = enumeration,
__newindex = function(self: any, index: any): ()
error(`{index} cannot be assigned to`, 0)
end,
__tostring = function(): string
return name
end,
}) :: any
end
return Enumeration
- EnumerationItem (inside Enumerations)
--!strict
local EnumerationItem = {}
export type EnumerationItem<T, N, V> = {
Name: N,
Value: V,
EnumType: T
}
function EnumerationItem.new<T, N, V>(name: N, value: V, enumType: T): EnumerationItem<T, N, V>
local enumerationItem: EnumerationItem<T, N, V> = {
Name = name,
Value = value,
EnumType = enumType
}
return setmetatable({}, {
__index = enumerationItem,
__newindex = function(self: any, index: any)
error(`{index} cannot be assigned to`, 0)
end,
__tostring = function(): string
return `{script.Parent.Name}.{getmetatable(enumerationItem.EnumType :: any).__name}.{name}`
end,
}) :: any
end
return EnumerationItem
I then just require my Enumerations and use it like I would with Enum
:
--!strict
local Enumerations = require(script.Parent)
-- this will have full intellisense, even if you do
-- upDirection.EnumType
local upDirection: Enumerations.Direction = Enumerations.Direction.Up
-- this will error since it's not an action
local downDirection: Enumerations.Action = Enumerations.Direction.Down
local function someCallbackFunction(direction: Enumerations.Direction): ()
if direction == upDirection then
-- do something
else
-- do something else
end
end
Unfortunately, I couldn’t do something like this as intellisense would just declare every type/generics as any
and their types, and I couldn’t figure out a pattern that allows me to pass the enumType
with this format without resorting to creating a whole different Enumeration class that is a copy of itself:
Actually I solved this. See latest edit for updated examples