Help with typechecking custom enums module

How could I go about type checking for members of a table?

I’m asking because I made a very simple enums module for the sake of convenience and not misspelling things between scripts:

local Enums = {}
Enums.GameState = {
	Intermission = "Intermission",
	Voting = "Voting",
	RoundPreparation = "RoundPreparation",
	RoundInProgress = "RoundInProgress",
}

...

return Enums

While it autofills when being used in code, I don’t really have a type check for it when I’m using it as a function argument. It’s just a string.

So when I have a function expecting a GameState:

local function setGameState(newGameState : string)
	-- do stuff
end

… the type checker only expects that newGameState is a string, which is technically correct. But what I want is for the type checker to recognize that newGameState must be a member of Enums.GameState.

Now I know there’s a workaround (that I saw in a tweet somewhere):


By explicitly defining the type as a union of string literals, I get the result I’m looking for.
But that means I’ll have to make sure that the table and the type match whenever I make any changes.

I feel like that’s not the way to go about things just because it’s more maintenance needed for such a small thing, almost like there’s a smarter way to do it that I just don’t know.

I’ve read the Luau type checking page maybe 10 times now and I still can’t think of a way. I don’t even understand the type packs/generics thing, maybe that’s what I actually need.

Is there a more efficient way to do this? Or a more proper way? I know it’s not that important, I can live with it honestly, but it bugs me to no end. I’ve tried searching here in the forum but I haven’t found anyone with the same situation. Someone put me out of my misery.

1 Like

This doesn’t have any affect on how your code executes, so your best bet is to add an else case, if the string is not one of the valid options, that errors.

1 Like

I know, that’s why I said I could live with it. I’m not really looking to add an error case, just putting this out there for someone who knows more about the typechecker than I do

1 Like

i feel like making an enum is so overkill when the same thing can be accomplished with variables, like what @azqjanna said too you can just handle iterating through the list with if and elseif boolean statements

1 Like

I like having autofill, it keeps me sane

Like I said I know it’s not really necessary, but this is more about the typechecker than my code setup

3 Likes

Actually, using enums is a good way to categorize multiple constants instead of using variables. They also help to keep the code clean.

I will show you a way to type check members of a table which is probably the way you’re looking for.

Here is what your ModuleScript would contain :

--!strict
export type GameState = {Value: string}

local Enums = {}

Enums.GameState = {
	Intermission = {Value = "Intermission"},
	Voting = {Value = "Voting"},
	RoundPreparation = {Value = "RoundPreparation"},
	RoundInProgress = {Value = "RoundInProgress"},
}

return Enums

And here is what happen when you’re trying to compare the exported GameState type defined in the ModuleScript with another type.
image

As you can see, the type inference engine detects when they are not the same type and says that it cannot be compared. You can add/remove game states from the enum without having to modify the GameState type.

Here is the correct way to compare a GameState type.
image

  • Do not forget to write the tag --!strict at the top in order to allow the type inference engine to show such conflicts (Type checking - Luau).
1 Like

A year later, I’ve managed to understand type generics and found a not-so-elegant-but-works-for-me solution:

local EnumsModule = {}

EnumsModule.newStringEnum = function<T>(enumTable: T)
	local stringEnumTable = {}
	for key,val in enumTable do 
		stringEnumTable[key] = key
	end
	table.freeze(stringEnumTable)
	return stringEnumTable :: T
end

return EnumsModule

image

It requires me to type in the enum “table” values with empty strings (or any string as it’s just used for the type inference anyway), but the end result is where I only type it out once and autofill works. Ain’t that just nice?

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.