The @enum annotation marks a Lua table as an enumeration type, providing both compile-time type checking and runtime-available enumeration values.
Syntax
-- Value enumeration (using table values)
---@enum <enum_name>
-- Key enumeration (using table keys)
---@enum (key) <enum_name>
Value Enumeration
Use table values as the enumeration values:
---@enum HTTPStatus
local HTTPStatus = {
OK = 200,
NOT_FOUND = 404,
INTERNAL_ERROR = 500,
BAD_REQUEST = 400,
UNAUTHORIZED = 401
}
---@param status HTTPStatus HTTP status code
---@return string Status description
function getStatusMessage(status)
if status == HTTPStatus.OK then
return "Request successful"
elseif status == HTTPStatus.NOT_FOUND then
return "Resource not found"
elseif status == HTTPStatus.INTERNAL_ERROR then
return "Internal server error"
else
return "Unknown status"
end
end
-- Usage
local response = {status = HTTPStatus.OK}
print(getStatusMessage(response.status)) -- "Request successful"
How it works:
- The
HTTPStatus type accepts the numeric values (200, 404, 500, etc.)
- Autocomplete suggests
HTTPStatus.OK, HTTPStatus.NOT_FOUND, etc.
- The table is available at runtime for lookups and iteration
Use value enums when you need to work with the actual values (like HTTP status codes) in your logic and APIs.
String Value Enumeration
---@enum LogLevel
local LogLevel = {
DEBUG = "debug",
INFO = "info",
WARN = "warn",
ERROR = "error",
FATAL = "fatal"
}
---@param level LogLevel Log level
---@param message string Log message
function writeLog(level, message)
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
print(string.format("[%s] %s: %s", timestamp, level, message))
end
-- Usage
writeLog(LogLevel.INFO, "Application started")
writeLog(LogLevel.ERROR, "Database connection failed")
Output:
[2024-01-15 10:30:45] info: Application started
[2024-01-15 10:30:46] error: Database connection failed
Key Enumeration
Use table keys as the enumeration values:
---@enum (key) Permission
local Permission = {
READ = true,
WRITE = true,
DELETE = true,
ADMIN = true
}
---@param user table User object
---@param permission Permission Permission type
---@return boolean Whether has permission
function hasPermission(user, permission)
return user.permissions and user.permissions[permission]
end
-- Usage
local currentUser = {
id = 1001,
name = "John",
permissions = {
[Permission.READ] = true,
[Permission.WRITE] = true
}
}
if hasPermission(currentUser, Permission.WRITE) then
print("User can write")
end
How key enums work:
- The
Permission type accepts the keys as strings: "READ", "WRITE", etc.
- Use
Permission.READ to reference the key name
- Useful when the actual values don’t matter, only the keys
Key enums are perfect for permission systems, feature flags, or any case where you only care about the presence or absence of a value.
Mixed Type Enumeration
Enumerations can contain different value types:
---@enum TaskStatus
local TaskStatus = {
PENDING = 0,
RUNNING = "running",
COMPLETED = true,
FAILED = false
}
---@param status TaskStatus Task status
function handleTaskStatus(status)
if status == TaskStatus.PENDING then
print("Task is waiting to start")
elseif status == TaskStatus.RUNNING then
print("Task is currently running")
elseif status == TaskStatus.COMPLETED then
print("Task completed successfully")
elseif status == TaskStatus.FAILED then
print("Task failed to complete")
end
end
handleTaskStatus(TaskStatus.RUNNING)
While mixed-type enums are supported, they can be confusing. Consider using consistent types for enum values when possible.
Enumeration Iteration
Since enums are real Lua tables, you can iterate over them:
---@enum LogLevel
local LogLevel = {
DEBUG = "debug",
INFO = "info",
WARN = "warn",
ERROR = "error",
FATAL = "fatal"
}
print("Available log levels:")
for name, value in pairs(LogLevel) do
print(string.format(" %s = %s", name, value))
end
Output:
Available log levels:
DEBUG = debug
INFO = info
WARN = warn
ERROR = error
FATAL = fatal
Practical Examples
Game State Management
---@enum GameState
local GameState = {
MENU = "menu",
PLAYING = "playing",
PAUSED = "paused",
GAME_OVER = "game_over"
}
local currentState = GameState.MENU
---@param newState GameState
function changeState(newState)
print("Changing state from", currentState, "to", newState)
currentState = newState
end
-- Usage
changeState(GameState.PLAYING)
changeState(GameState.PAUSED)
HTTP Client
---@enum HTTPMethod
local HTTPMethod = {
GET = "GET",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
PATCH = "PATCH"
}
---@param method HTTPMethod
---@param url string
---@param data table?
---@return table
function httpRequest(method, url, data)
return {
method = method,
url = url,
data = data
}
end
-- Usage
local request = httpRequest(HTTPMethod.POST, "/api/users", {
name = "John",
email = "[email protected]"
})
User Roles and Permissions
---@enum (key) Role
local Role = {
GUEST = true,
USER = true,
MODERATOR = true,
ADMIN = true
}
---@param role Role
---@return number
function getRolePriority(role)
local priorities = {
[Role.GUEST] = 0,
[Role.USER] = 1,
[Role.MODERATOR] = 2,
[Role.ADMIN] = 3
}
return priorities[role] or 0
end
---@param userRole Role
---@param requiredRole Role
---@return boolean
function hasRequiredRole(userRole, requiredRole)
return getRolePriority(userRole) >= getRolePriority(requiredRole)
end
-- Usage
if hasRequiredRole(Role.MODERATOR, Role.USER) then
print("Access granted")
end
Configuration Options
---@enum Environment
local Environment = {
DEVELOPMENT = "dev",
STAGING = "staging",
PRODUCTION = "prod"
}
---@param env Environment
---@return table
function getConfig(env)
local configs = {
[Environment.DEVELOPMENT] = {
apiUrl = "http://localhost:3000",
debug = true
},
[Environment.STAGING] = {
apiUrl = "https://staging.example.com",
debug = true
},
[Environment.PRODUCTION] = {
apiUrl = "https://api.example.com",
debug = false
}
}
return configs[env]
end
-- Usage
local config = getConfig(Environment.DEVELOPMENT)
print("API URL:", config.apiUrl)
Enum vs Alias
Understanding when to use @enum vs @alias:
Use @enum when:
- You need runtime access to the values
- You want to iterate over the enumeration
- The values are constants defined in a table
---@enum Status
local Status = {
ACTIVE = "active",
INACTIVE = "inactive"
}
-- Can iterate at runtime
for k, v in pairs(Status) do
print(k, v)
end
Use @alias when:
- You only need compile-time type checking
- You want to document valid string literals
- You don’t need runtime access
---@alias Status
---| 'active'
---| 'inactive'
-- No runtime table, just type information
If you need both compile-time type safety AND runtime enumeration, use @enum. If you only need autocomplete and type checking, use @alias.
Validation with Enums
---@enum Color
local Color = {
RED = "red",
GREEN = "green",
BLUE = "blue"
}
---@param color string
---@return boolean
function isValidColor(color)
for _, validColor in pairs(Color) do
if color == validColor then
return true
end
end
return false
end
-- Usage
if isValidColor("red") then
print("Valid color")
end
Best Practices
- Use UPPERCASE names: Convention for enum keys is SCREAMING_SNAKE_CASE
- Group related values: Keep enums focused on a single concept
- Document the purpose: Add comments explaining when to use each value
- Choose value vs key wisely: Use values when the actual value matters
- Make enums readonly: Don’t modify enum tables after creation
- Use consistent value types: Avoid mixing types unless necessary
Common Patterns
Default Values
---@enum Priority
local Priority = {
LOW = 1,
NORMAL = 2,
HIGH = 3,
URGENT = 4
}
Priority.DEFAULT = Priority.NORMAL
Reverse Lookup
---@enum HTTPStatus
local HTTPStatus = {
OK = 200,
NOT_FOUND = 404,
ERROR = 500
}
-- Create reverse mapping
local HTTPStatusName = {}
for name, code in pairs(HTTPStatus) do
HTTPStatusName[code] = name
end
print(HTTPStatusName[200]) -- "OK"
Enums provide type information to the language server, but don’t prevent invalid runtime values. Always validate user input.