The @alias annotation allows you to define type aliases for creating custom types, enumeration types, or simplifying complex type expressions.
Syntax
-- Simple alias
---@alias <alias> <type_expression>
-- Enumeration alias
---@alias <alias>
---| '<value1>' [# description1]
---| '<value2>' [# description2]
---| ...
-- Generic alias
---@alias <alias><<generic_parameter_list>> <type_expression>
Simple Type Aliases
Create descriptive names for primitive types:
---@alias ID number
---@alias UserName string
---@param userId ID
---@param name UserName
function createUser(userId, name)
-- userId is a number, but semantically represents an ID
-- name is a string, but semantically represents a username
end
Benefits:
- Makes code more self-documenting
- Clarifies the semantic meaning of types
- Easier to refactor if the underlying type changes
Use type aliases to add semantic meaning to basic types. ID is more descriptive than number when representing user identifiers.
Union Type Aliases
Simplify complex union types:
---@alias StringOrNumber string | number
---@alias MaybeString string | nil
---@param value StringOrNumber
---@return string
function toString(value)
return tostring(value)
end
---@param name MaybeString
---@return string
function getDisplayName(name)
return name or "Anonymous"
end
Enumeration Aliases
Define string literal enumerations with descriptions:
---@alias HTTPMethod
---| 'GET' # HTTP GET request
---| 'POST' # HTTP POST request
---| 'PUT' # HTTP PUT request
---| 'DELETE' # HTTP DELETE request
---| 'PATCH' # HTTP PATCH request
---@param method HTTPMethod
---@param url string
function makeRequest(method, url)
-- method can only be one of the specified values
-- IDE will provide autocomplete for these values
end
-- Usage
makeRequest('GET', 'https://api.example.com/users')
makeRequest('INVALID', 'url') -- Warning: 'INVALID' is not a valid HTTPMethod
Line-by-line explanation:
- Line 1: Define the alias name
HTTPMethod
- Lines 2-6: Each
---| 'value' line adds a possible string literal
- The
# comment after each value provides a description
- This enables autocomplete and type checking
Add descriptions after # to provide context in autocomplete suggestions. This helps other developers understand when to use each value.
Status Enumeration Example
---@alias TaskStatus
---| 'pending' # Waiting for execution
---| 'running' # Currently executing
---| 'completed' # Execution completed
---| 'failed' # Execution failed
---@param status TaskStatus
function updateTaskStatus(status)
print("Task status:", status)
end
updateTaskStatus('running') -- OK
updateTaskStatus('cancelled') -- Warning: not a valid TaskStatus
Generic Aliases
Create reusable parameterized types:
---@alias Result<T, E> {success: boolean, data: T, error: E}
---@alias Array<T> T[]
---@alias Dictionary<K, V> table<K, V>
---@type Result<string, string>
local result = {
success = true,
data = "Hello",
error = nil
}
---@type Array<number>
local numbers = {1, 2, 3, 4, 5}
---@type Dictionary<string, number>
local ages = {
["John"] = 30,
["Jane"] = 25
}
Benefits of generic aliases:
- Define the pattern once, reuse with different types
- Clearer than repeating complex type expressions
- Easier to maintain and refactor
Function Type Aliases
Simplify complex function signatures:
---@alias EventHandler fun(event: string, ...): boolean
---@alias AsyncCallback<T> fun(error: string?, result: T?): nil
---@param handler EventHandler
function registerEventHandler(handler)
-- handler is a function that takes an event name and returns boolean
end
---@param callback AsyncCallback<string>
function fetchData(callback)
-- Simulated async operation
local success = true
if success then
callback(nil, "Data retrieved")
else
callback("Error occurred", nil)
end
end
Usage:
-- EventHandler usage
registerEventHandler(function(event, ...)
print("Event:", event)
return true
end)
-- AsyncCallback usage
fetchData(function(error, result)
if error then
print("Error:", error)
else
print("Result:", result)
end
end)
Practical Examples
API Response Types
---@alias APIResponse<T>
---| {success: true, data: T}
---| {success: false, error: string}
---@return APIResponse<User>
function getUser(id)
if id > 0 then
return {success = true, data = {id = id, name = "John"}}
else
return {success = false, error = "Invalid user ID"}
end
end
-- Usage
local response = getUser(123)
if response.success then
print("User:", response.data.name)
else
print("Error:", response.error)
end
Configuration Types
---@alias Environment
---| 'development' # Development environment
---| 'staging' # Staging environment
---| 'production' # Production environment
---@alias LogLevel
---| 'debug'
---| 'info'
---| 'warn'
---| 'error'
---@alias AppConfig {
--- env: Environment,
--- logLevel: LogLevel,
--- apiUrl: string,
--- timeout: number
---}
---@type AppConfig
local config = {
env = 'development',
logLevel = 'debug',
apiUrl = 'http://localhost:3000',
timeout = 5000
}
Validation Types
---@alias Validator<T> fun(value: T): boolean, string?
---@type Validator<string>
local emailValidator = function(value)
if value:match("^[%w.]+@[%w.]+%.%w+$") then
return true
else
return false, "Invalid email format"
end
end
---@type Validator<number>
local ageValidator = function(value)
if value >= 0 and value <= 120 then
return true
else
return false, "Age must be between 0 and 120"
end
end
Complex Type Simplification
Replace verbose inline types with aliases:
Before:
---@param user {id: number, name: string, email: string, permissions: {read: boolean, write: boolean, delete: boolean}}
function processUser(user)
-- ...
end
After:
---@alias Permissions {read: boolean, write: boolean, delete: boolean}
---@alias User {id: number, name: string, email: string, permissions: Permissions}
---@param user User
function processUser(user)
-- Much more readable!
end
Type aliases are compile-time only. They don’t create new types at runtime or perform validation. Always validate user input explicitly.
Best Practices
- Use descriptive names:
HTTPMethod is better than Method or HTTP
- Add descriptions to enum values: Help other developers understand when to use each value
- Keep aliases focused: One alias should represent one concept
- Document complex aliases: Add comments explaining the purpose
- Prefer aliases over repeated types: If you type it more than twice, make it an alias
- Use generic aliases for patterns:
Result<T, E> is better than separate StringResult, NumberResult, etc.
Common Use Cases
- Simplifying complex type expressions: Make code more readable
- Creating enumeration types: Provide autocomplete for valid values
- Defining common data structures: Reuse across your codebase
- Improving code readability and maintainability: Self-documenting types
- API contracts: Define request/response shapes
- Configuration schemas: Type-safe configuration objects
Combine aliases with @param and @return annotations to create clear, self-documenting function signatures.