Menu
CoddyTech

Luau 速查表

类型检查模式

文件第一行的注释决定 Luau 检查得有多严格。

模式语法
不做类型检查--!nocheck
默认:只检查有注解的部分--!nonstrict
严格:推断并检查所有内容--!strict
运行文件luau main.luau
只做类型检查,不运行luau-analyze main.luau
关闭某一条 lint 警告--!nolint UnknownGlobal

基本类型

基本类型,以及如何给变量加类型注解。

操作语法
数字local age: number = 25
字符串local name: string = "Ada"
布尔值local ok: boolean = true
可选类型(可能为 nil)local nick: string? = nil
Any(不做检查)local x: any = f()
Unknown(更安全的 any)local x: unknown = f()
Never(总是抛出错误)function fail(): never error("boom") end
Nillocal n: nil = nil
协程 / bufferlocal co: thread, local b: buffer
类型推断:不需要注解local hp = 100 -- number

带类型的 table:数组与 map

table 依然是 Lua 的 table,类型只是说明里面装的是什么。

操作语法
数字数组local xs: {number} = {1, 2, 3}
字符串数组local names: {string} = {}
map(字符串键)local ages: {[string]: number} = {}
map(任意键类型)local seen: {[Player]: boolean} = {}
嵌套数组local grid: {{number}} = {}
结构体数组local items: {{id: number}} = {}
只读数组local t = table.freeze({1, 2, 3})
通用遍历(Luau 独有)for i, v in xs do print(i, v) end
遍历键和值for k, v in pairs(ages) do end

函数

给参数和返回值加类型注解;-> () 表示没有返回值。

操作语法
带类型的参数和返回值function add(a: number, b: number): number
无返回值function log(msg: string): () end
返回多个值function split(s: string): (string, string)
可选参数function greet(name: string?) end
默认值function greet(name: string?) name = name or "friend" end
可变参数function sum(...: number): number end
匿名函数local f = function(x: number): number return x * 2 end
函数类型type Adder = (number, number) -> number
回调参数function each(f: (number) -> ()) end

类型别名、联合类型与字面量类型

给类型起一次名字,到处都能复用。

操作语法
给基本类型起别名type Health = number
联合类型type Id = number | string
可选类型就是与 nil 的联合type Maybe = string | nil -- same as string?
字面量(单例)类型type Dir = "up" | "down"
交叉类型type Both = Named & Aged
泛型别名type List<T> = {T}
从模块导出export type Point = { x: number, y: number }
取现有值的类型type Config = typeof(config)
table 类型的所有键type K = keyof<Point>

table 结构与方法

描述对象的结构,以及冒号调用带来的 self。

操作语法
内联结构local p: { x: number, y: number } = { x = 0, y = 0 }
命名结构type Point = { x: number, y: number }
可选属性type User = { name: string, age: number? }
结构 + 索引器type Bag = { count: number, [string]: any }
扩展结构type Admin = User & { level: number }
方法(冒号简写)function Point.move(self: Point, dx: number) end
类型中的方法type Point = { move: (self: Point, dx: number) -> () }
构造函数写法function Point.new(x: number): Point end
冻结的常量(类似枚举)local Color = table.freeze({ Red = 1, Blue = 2 })

泛型

一个函数或类型,适用于多种值类型。

操作语法
泛型函数function id<T>(x: T): T return x end
两个类型参数function pair<A, B>(a: A, b: B): (A, B) end
针对数组的泛型function first<T>(xs: {T}): T? return xs[1] end
泛型类型别名type Stack<T> = { items: {T} }
实例化别名local s: Stack<string> = { items = {} }
泛型 map 函数function map<T, U>(xs: {T}, f: (T) -> U): {U} end
泛型包(可变参数)function call<T...>(f: () -> T...): T... end

类型收窄、断言与守卫

让类型检查器相信,这个值就是你知道的那种类型。

操作语法
类型断言(转换)local n = value :: number
经由 any 断言local n = (value :: any) :: number
用 typeof 收窄if typeof(x) == "string" then -- x is string end
收窄可选类型if name then print(#name) end
为 nil 时提前返回if not name then return end
收窄字面量联合类型if dir == "up" then end
运行时检查 + 类型错误assert(typeof(id) == "number", "id must be a number")
断言不为 nil 后再使用local item = assert(find(id), "missing item")

Lua 没有的运算符与语法

这些日常用得上的便利写法,让 Luau 代码看起来不再像 Lua。

操作语法
加后赋值hp += 10
减 / 乘 / 除后赋值hp -= 5, dmg *= 2, dmg /= 2
整除后赋值n //= 2
取模 / 乘方后赋值n %= 3, n ^= 2
拼接后赋值msg ..= "!"
整除local half = 7 // 2 -- 3
字符串插值Hello, {name}! You have {n} items.(用反引号包起来)
插入表达式Total: {price * qty}(用反引号包起来)
跳过本次循环for _, v in xs do if v < 0 then continue end end
if-then-else 表达式local label = if hp > 0 then "alive" else "dead"
链式 else-if 表达式local t = if n > 0 then "pos" elseif n < 0 then "neg" else "zero"
二进制 / 带分隔符的数字字面量0b1010, 1_000_000

新增库函数与和 Lua 的区别

Luau 新增的标准库函数,以及它移除的 Lua 特性。

项目说明
在数组中查找值table.find(xs, 42) -- index or nil
浅拷贝local copy = table.clone(t)
设为不可变 / 检查table.freeze(t), table.isfrozen(t)
预分配数组table.create(10, 0)
限制范围 / 四舍五入 / 取符号math.clamp(x, 0, 1), math.round(x), math.sign(x)
位运算bit32.band(a, b), bit32.lshift(a, 1)
为沙箱安全而移除io, package, loadstring, loadfile, dofile
已弃用但仍可用getfenv, setfenv(会让优化失效)
Luau 中没有goto / 标签(Luau 基于 Lua 5.1)
在 Coddy 上读取标准输入io.read() 在这里可以用,Coddy 在 Luau 之上恢复了它
文件扩展名main.luau(.lua 也可以)
Roblox 独有,不属于核心 Luaugame, workspace, Instance.new, task.wait

Luau 就是 Lua 5.1 加上一套渐进式类型系统,由 Roblox 创建并开源。这份 Luau 速查表讲的是普通 Lua 里没有的部分:类型注解、带类型的 table、联合类型和字面量类型、泛型、类型断言,以及 Luau 新增的语法糖(+=、字符串插值、continue、if-then-else 表达式)。两种语言共有的内容,比如 table、元表、协程和 string 库,请对照旁边的 Lua 速查表。

这里的每个代码片段都是标准 Luau,可以用 luau 命令行运行,也可以用 luau-analyze 做类型检查。需要什么就复制什么,也可以在 Luau 在线编辑器里直接运行,无需安装。想从零学会类型系统?Coddy 的免费交互式 Luau 课程会一课一课带你掌握。

Luau 速查表常见问题

这份 Luau 速查表免费吗?

免费。这份 Luau 速查表完全免费,也不用注册。把它加入书签,下次需要查联合类型、泛型或带类型 table 的写法时就能直接打开。

Luau 有 += 运算符吗?
有。和普通 Lua 不同,Luau 支持复合赋值:+=、-=、*=、/=、//=、%=、^=,还有用于字符串拼接的 ..=。所以 hp += 10 是合法的 Luau 代码(也是合法的 Roblox 脚本),而在 Lua 5.1 中你必须写成 hp = hp + 10。注意,Luau 中没有 ++ 或 --。
Lua 和 Luau 有什么区别?
Luau 是 Lua 5.1 的超集,带有渐进式类型系统。除了类型,它还新增了用反引号写的字符串插值、复合赋值运算符、continue、if-then-else 表达式、整除 //、通用遍历(for i, v in t do),以及 table.find、table.clone、table.freeze 等库函数。为了沙箱安全,它移除了 loadstring、loadfile、dofile 以及 io 和 package 库;又因为基于 5.1,它也没有 goto。大多数普通 Lua 代码不用修改就能作为 Luau 运行。
怎么在 Luau 中开启类型检查?
在文件第一行写一条模式注释:--!strict 会检查所有内容并积极推断类型,--!nonstrict(默认)只检查你加了注解的部分,--!nocheck 则关闭检查。在编辑器之外,运行 luau-analyze main.luau 可以只做类型检查而不执行代码,运行 luau main.luau 则会执行它。
Luau 里也像 TypeScript 那样写 interface 吗?
不用。Luau 没有 interface 关键字。对象的结构用 table 类型来写,再用 type 起个名字:type User = { name: string, age: number? }。组合多个结构时用 &,而不是 extends;想让其他模块也能用,就写 export type。
这份速查表和 Roblox 脚本是一回事吗?
这里的内容都是核心 Luau(也就是语言本身),所以 Roblox 脚本和独立的 Luau 都适用。这里没有的是 Roblox 引擎 API(game、workspace、Instance.new、各种服务和事件),它是建立在语言之上的一个库,由 Roblox 而不是 Luau 提供文档。Roblox 游戏脚本课程会教你这套 API,你写的脚本会在浏览器里的一个 Roblox 风格小游戏中运行。
在哪里可以运行这些 Luau 代码片段?
在 Luau 在线编辑器里。它在你的浏览器中运行真正的 luau 解释器,并支持标准输入,你可以把本页的任意一行粘贴进去,然后点击“运行”。想系统地练习,交互式 Luau 课程用练习、项目和证书覆盖了同样的内容。
Coddy 编程语言插图

在 Coddy 学习 Luau

开始