Skip to main content
The @param annotation documents function parameters with type information, enabling IDE autocomplete, type checking, and inline documentation.

Syntax

---@param <parameter_name>[?] <type_expression> [description]
  • ? - Marks the parameter as optional
  • ... - Indicates variadic parameters
  • Supports union types, generics, and complex type expressions

IDE Benefits

function createUser(name, age)
    return {name = name, age = age}
end

-- No IDE support:
-- ❌ No parameter type hints
-- ❌ No autocomplete suggestions
-- ❌ No type validation
-- ❌ No inline documentation

Basic Parameter Types

1
Simple Types
2
Document basic parameters with type and description:
3
---@param name string Username
---@param age number Age in years
---@param active boolean Account status
function createUser(name, age, active)
    return {
        name = name,
        age = age,
        active = active
    }
end
4
Optional Parameters
5
Use ? to mark parameters as optional:
6
---@param name string Username
---@param age? number Age (optional, defaults to 18)
---@param email? string Email address (optional)
function registerUser(name, age, email)
    age = age or 18
    return {
        name = name,
        age = age,
        email = email
    }
end

-- Usage
local user1 = registerUser("John")  -- age and email are optional
local user2 = registerUser("Jane", 25)  -- email is optional
local user3 = registerUser("Bob", 30, "[email protected]")
7
Union Types
8
Allow multiple type options:
9
---@param id string | number User ID (string or number)
---@param options table | nil Configuration options
function getUserById(id, options)
    local normalizedId = tostring(id)
    options = options or {}
    -- IDE knows id can be string OR number
end

Advanced Parameter Types

Variadic Parameters

Document functions that accept variable arguments:
---@param format string Format string
---@param ... any Formatting arguments
function printf(format, ...)
    print(string.format(format, ...))
end

-- Usage
printf("Hello %s, you are %d years old", "Alice", 25)

Function Parameters

Define callback signatures with parameter and return types:
---@param data table Data to process
---@param callback fun(result: any, error: string?): nil
function processDataAsync(data, callback)
    local success, result = pcall(function()
        return processData(data)
    end)
    
    if success then
        callback(result, nil)
    else
        callback(nil, result)  -- result is error message
    end
end

Complex Object Parameters

Define inline table structures:
---@param request {method: string, url: string, headers?: table<string, string>, body?: string}
---@param options? {timeout?: number, retries?: number}
function httpRequest(request, options)
    options = options or {}
    local timeout = options.timeout or 30  -- IDE autocompletes 'timeout'
    local retries = options.retries or 3    -- IDE autocompletes 'retries'
    
    -- IDE validates request fields:
    print(request.method)  -- ✅ Valid
    print(request.url)     -- ✅ Valid
    print(request.invalid) -- ⚠️ Warning: field doesn't exist
end

-- Usage with autocomplete
httpRequest({
    method = "GET",  -- IDE suggests 'method', 'url', 'headers', 'body'
    url = "https://api.example.com/users",
    headers = {
        ["Authorization"] = "Bearer token123"
    }
}, {
    timeout = 60,  -- IDE suggests 'timeout' and 'retries'
    retries = 5
})

Generic Parameters

Create type-safe generic functions:
---@generic T
---@param items T[] List of items
---@param predicate fun(item: T): boolean Filter predicate
---@return T[] Filtered list
function filter(items, predicate)
    local result = {}
    for _, item in ipairs(items) do
        if predicate(item) then
            table.insert(result, item)
        end
    end
    return result
end

-- Usage: Type information flows through
local numbers = {1, 2, 3, 4, 5}
local evens = filter(numbers, function(n)
    return n % 2 == 0  -- IDE knows 'n' is number
end)
-- IDE knows 'evens' is number[]

Method Parameters

Explicit self Parameter

---@class Calculator
local Calculator = {}

---@param self Calculator
---@param x number First number
---@param y number Second number
---@return number Result
function Calculator.add(self, x, y)
    return x + y
end

Colon Syntax (self auto-inferred)

---@param x number First number
---@param y number Second number
---@return number Result
function Calculator:multiply(x, y)
    -- self is automatically inferred
    return x * y
end

Before/After Comparison

Without @param

function processData(data, callback, options)
    -- No type information
    -- No IDE support
    -- Errors caught at runtime
end

processData(
    "wrong type",  -- ❌ No warning
    123,           -- ❌ No warning
    "invalid"      -- ❌ No warning
)
Problems:
  • No autocomplete
  • Type errors go undetected
  • No documentation
  • Hard to understand API

With @param

---@param data table Data to process
---@param callback fun(result: any): nil
---@param options? {timeout: number}
function processData(data, callback, options)
    -- Full type information
    -- IDE support enabled
    -- Errors caught before runtime
end

processData(
    "wrong type",  -- ⚠️ Warning: expected table
    123,           -- ⚠️ Warning: expected function
    "invalid"      -- ⚠️ Warning: expected table
)
Benefits:
  • Full autocomplete support
  • Type validation
  • Inline documentation
  • Clear API contract

Complete Example

---@class Request
---@field method string
---@field url string
---@field headers? table<string, string>
---@field body? string

---@class Response
---@field status number
---@field data any
---@field headers table<string, string>

---@param request Request HTTP request configuration
---@param options? {timeout?: number, retries?: number, validateSSL?: boolean}
---@param onProgress? fun(bytesReceived: number, totalBytes: number): nil
---@return Response | nil response
---@return string? error
function httpRequest(request, options, onProgress)
    options = options or {}
    local timeout = options.timeout or 30
    local retries = options.retries or 3
    local validateSSL = options.validateSSL ~= false
    
    local totalBytes = 1024
    local bytesReceived = 0
    
    -- Report progress
    if onProgress then
        onProgress(bytesReceived, totalBytes)
    end
    
    -- Perform request
    local success, result = pcall(function()
        return performRequest(request, timeout, validateSSL)
    end)
    
    if success then
        return result, nil
    else
        return nil, "Request failed: " .. tostring(result)
    end
end

-- Usage with full IDE support
local response, err = httpRequest({
    method = "POST",  -- Autocompletes: method, url, headers, body
    url = "https://api.example.com/data",
    headers = {
        ["Content-Type"] = "application/json",
        ["Authorization"] = "Bearer token"
    },
    body = '{"key": "value"}'
}, {
    timeout = 60,      -- Autocompletes: timeout, retries, validateSSL
    retries = 5,
    validateSSL = true
}, function(received, total)
    -- IDE knows parameter types
    local percent = (received / total) * 100
    print(string.format("Progress: %.1f%%", percent))
end)

if response then
    print("Status:", response.status)  -- IDE autocompletes response fields
    print("Data:", response.data)
else
    print("Error:", err)
end

Best Practices

Every public API function should have parameter annotations for discoverability and type safety.
Mark parameters as optional only when they truly have default values or can be nil.
Descriptions help users understand the parameter’s purpose beyond just its type.
When a parameter can accept multiple types, use union types (e.g., string | number) instead of any.
Inline table definitions provide better autocomplete than just using table.

See Also

  • @return - Document function return values
  • @overload - Define multiple function signatures
  • @generic - Create generic type parameters

Build docs developers (and LLMs) love