Skip to main content

Basic Syntax

All EmmyLua annotations start with the ---@ prefix (three dashes followed by @). This distinguishes them from regular comments:
-- This is a regular comment

--- This is a documentation comment (LuaDoc style)

---@type string
-- This is an EmmyLua annotation

Annotation Format

The general structure of an annotation is:
---@annotation_name parameters description
Each part serves a specific purpose:
  • ---@ - The annotation prefix
  • annotation_name - The type of annotation (class, param, return, etc.)
  • parameters - Required parameters for the annotation
  • description - Optional human-readable description

Comment Format

Single-Line Annotations

Most annotations fit on a single line:
---@type string
local name = "John"

---@param x number
---@param y number
---@return number
function add(x, y)
    return x + y
end

Multi-Line Annotations

Some annotations span multiple lines. Each line must start with ---@ or ---| :
---@alias HTTPMethod
---| 'GET'     # HTTP GET request
---| 'POST'    # HTTP POST request
---| 'PUT'     # HTTP PUT request
---| 'DELETE'  # HTTP DELETE request
---@class User
---@field id number User ID
---@field name string Username
---@field email string Email address
---@field role 'admin'|'user'|'guest' User role
The ---| prefix (three dashes, pipe, space) is used for enumeration values in @alias annotations.

Annotation Placement

Annotations must be placed immediately before the item they describe, with no blank lines in between.

Annotating Variables

Place the annotation directly above the variable:
---@type string
local userName = "John"

---@type number
local userAge = 25

---@type {name: string, age: number}
local user = {name = "John", age = 25}

Annotating Functions

Place annotations directly above the function definition:
---@param name string Username
---@param age number User age
---@return table User object
function createUser(name, age)
    return {name = name, age = age}
end

Annotating Classes

Define the class structure, then the implementation:
---@class Animal
---@field name string Animal name
---@field species string Species
---@field age number Age in years
local Animal = {}

---@param name string
---@param species string
---@return Animal
function Animal.new(name, species)
    return setmetatable({
        name = name,
        species = species,
        age = 0
    }, {__index = Animal})
end

Annotating Fields and Methods

---@class Database
local Database = {}

---@type string
Database.host = "localhost"

---@type number
Database.port = 5432

---@param query string SQL query
---@return table[] Result rows
function Database:execute(query)
    -- Implementation
end

Annotation Structure

Syntax Notation Symbols

The documentation uses these symbols to describe annotation syntax:
SymbolMeaningExample
<name>Required placeholder---@param <param_name> <type>
[value]Optional item---@param name string [description]
[value...]Optional and repeatable---@class Name [: Parent...]
value1 | value2Choice (either/or)'GET' | 'POST'
<type[|type...]>Type expression with unionsstring | number
[(modifier)]Optional modifier---@class (exact) Point
#Comment marker---| 'GET' # Description

Required vs Optional Parameters

Some annotation parameters are required, others are optional:
-- @type requires only the type
---@type string

-- @param requires name and type, description is optional
---@param name string
---@param age number User age in years

-- @class requires name, parent classes are optional
---@class User
---@class Admin : User
---@class SuperAdmin : Admin, Auditable

Type Expressions

Type expressions can be simple or complex:
-- Simple types
---@type string
---@type number
---@type boolean

-- Union types
---@type string | number
---@type string | nil

-- Array types
---@type string[]
---@type number[]

-- Table types
---@type table<string, number>
---@type table<number, string>

-- Function types
---@type fun(x: number): string
---@type fun(name: string, age: number): User

-- Tuple types
---@type [string, number, boolean]

-- Table literal types
---@type {name: string, age: number}

-- Complex combinations
---@type table<string, fun(x: number): boolean> | nil

Common Patterns

Pattern 1: Variable Type Declaration

---@type <type_expression>
local variableName = value
Example:
---@type string
local userName = "John"

---@type number[]
local scores = {95, 87, 92}

Pattern 2: Function Documentation

---@param <param_name> <type> [description]
---@param <param_name> <type> [description]
---@return <type> [description]
function functionName(param1, param2)
    -- Implementation
end
Example:
---@param x number First number
---@param y number Second number
---@return number Sum of x and y
function add(x, y)
    return x + y
end

Pattern 3: Class Definition

---@class <ClassName> [: ParentClass]
---@field <field_name> <type> [description]
---@field <field_name> <type> [description]
local ClassName = {}
Example:
---@class User
---@field id number User ID
---@field name string Username
---@field email string Email address
local User = {}

Pattern 4: Type Alias

---@alias <AliasName> <type_expression>
Example:
---@alias ID number
---@alias Callback fun(error: string?, result: any?): nil
---@alias Status 'pending'|'running'|'completed'|'failed'

Pattern 5: Enumeration

---@alias <EnumName>
---| '<value1>' [# description]
---| '<value2>' [# description]
---| '<value3>' [# description]
Example:
---@alias LogLevel
---| 'debug'   # Debug messages
---| 'info'    # Informational messages
---| 'warn'    # Warning messages
---| 'error'   # Error messages

Pattern 6: Generic Function

---@generic <T>
---@param <param> <T>
---@return <T>
function functionName(param)
    -- Implementation
end
Example:
---@generic T
---@param items T[]
---@param predicate fun(item: T): boolean
---@return T[]
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

Annotation Chaining

Multiple annotations can be chained together to provide complete type information:

Class with Fields and Methods

---@class Calculator
---@field value number Current value
---@field history number[] Calculation history
local Calculator = {}

---@param initial number Starting value
---@return Calculator
function Calculator.new(initial)
    return setmetatable({
        value = initial,
        history = {initial}
    }, {__index = Calculator})
end

---@param x number Number to add
---@return Calculator self For method chaining
function Calculator:add(x)
    self.value = self.value + x
    table.insert(self.history, self.value)
    return self
end

Function with Multiple Parameters and Returns

---@param url string Request URL
---@param method 'GET'|'POST'|'PUT'|'DELETE' HTTP method
---@param headers? table<string, string> Optional headers
---@param body? string Optional request body
---@return boolean success Whether request succeeded
---@return string? response Response body if successful
---@return string? error Error message if failed
function httpRequest(url, method, headers, body)
    -- Implementation
end

Generic Class with Constraints

---@class Container<T>
---@field private items T[]
---@field capacity number
local Container = {}

---@generic T
---@param capacity number Maximum number of items
---@return Container<T>
function Container.new(capacity)
    return {
        items = {},
        capacity = capacity
    }
end

---@generic T
---@param self Container<T>
---@param item T
---@return boolean success Whether item was added
function Container:add(item)
    if #self.items < self.capacity then
        table.insert(self.items, item)
        return true
    end
    return false
end

Annotation Formats: EmmyLua vs LuaCATS

Both formats are supported and can be used interchangeably:

EmmyLua Format

The original format with ---@ prefix:
---@class Vector
---@field x number
---@field y number
local Vector = {}

---@param x number
---@param y number
---@return Vector
function Vector.new(x, y)
    return setmetatable({x = x, y = y}, {__index = Vector})
end

---@return number
function Vector:magnitude()
    return math.sqrt(self.x^2 + self.y^2)
end

LuaCATS Format

Documentation-focused with descriptive text:
--- Vector class for 2D coordinates
---@class Vector
---@field x number X coordinate
---@field y number Y coordinate
local Vector = {}

--- Create a new vector
---@param x number X coordinate
---@param y number Y coordinate
---@return Vector A new vector instance
function Vector.new(x, y)
    return setmetatable({x = x, y = y}, {__index = Vector})
end

--- Calculate the magnitude of the vector
---@return number The magnitude
function Vector:magnitude()
    return math.sqrt(self.x^2 + self.y^2)
end
You can mix both formats in the same project, but maintaining consistency improves readability.

Best Practices

1. Be Consistent

Choose a style and stick to it throughout your project:
-- Good: Consistent formatting
---@param name string
---@param age number
---@return User

-- Avoid: Inconsistent spacing
---@param name   string
---@param age number
---@return    User

2. Document Public APIs

Always annotate public functions and classes:
-- Public API - fully documented
---@class Database
---@field host string
---@field port number

---@param config {host: string, port: number}
---@return Database
function Database.new(config)
    -- Implementation
end

-- Private helper - can be less formal
local function parseConnectionString(str)
    -- Implementation
end

3. Use Descriptive Names

Choose clear, self-documenting parameter names:
-- Good
---@param userName string
---@param userAge number
function createUser(userName, userAge)
end

-- Less clear
---@param n string
---@param a number
function createUser(n, a)
end

4. Add Descriptions for Complex Types

---@param options {timeout: number, retries: number, backoff: boolean} Configuration options for the request. Timeout in seconds, retries is the max number of attempts, backoff enables exponential backoff
function httpRequest(url, options)
end
-- Good: Grouped by purpose
---@class User
---@field id number
---@field name string
---@field email string
---@field createdAt number
---@field updatedAt number

-- Less organized: Mixed ordering
---@class User
---@field name string
---@field id number
---@field updatedAt number
---@field email string
---@field createdAt number

Next Steps

Now that you understand annotation syntax, explore specific annotation types:

Type Annotations

Learn about @type and type expressions

Class Definitions

Define classes with @class and @field

Function Parameters

Document parameters with @param

Return Values

Specify return types with @return

Type Aliases

Create reusable types with @alias

Generics

Use generic types with @generic

Build docs developers (and LLMs) love