Type Annotation in Lua#
Overview#
Lua 语言本身并没有提供任何 type annotation 的支持. 不过对于不同的 IDE, 有一些工具可以提供 type annotation 的支持. 这些支持不会在运行的时候做任何强制性的检查, 而仅仅为 IDE 辅助和文档生成服务的.
在 JetBrain 家的 IDEA 中, EmmyLua 提供了语法高亮, 类型系统, debugger 等支持.
在 VSCode 生态中, lua-language-server (LuaLS) 也提供了类似的功能.
因为我的主力 IDE 是 IDEA, 所以我就选择 EmmyLua 了.
Resource#
我的学习资料主要来自于问 ChatGPT 和 Claude AI. 另外我还发现一个 EmmyLua Annotations 的中文文档很不错.
Example 1#
type_annotation_1.lua
1---@meta
2---@diagnostic disable: undefined-global
3---@diagnostic disable: lowercase-global
4
5--[[----------------------------------------------------------------------------
6标注一个变量
7--]]----------------------------------------------------------------------------
8---type annotation 要在声明的前一行
9---@type number
10local a_number = 1
11print(string.format("a_number = %s", a_number))
12
13---如果你要加一些描述, 直接在类型后面空格开始写即可
14---@type string your description here
15local a_string = "Alice"
16print(string.format("a_string = %s", a_string))
17
18---@type boolean
19local a_boolean = true
20print(string.format("a_boolean = %s", a_boolean))
21
22--[[----------------------------------------------------------------------------
23标注一个容器类型的 Table
24--]]----------------------------------------------------------------------------
25---@type table
26local a_list = { 1, 2, 3 }
27print(string.format("a_list = %s", a_list))
28
29---@type table
30local a_dict = { a = 1, b = 2, c = 3 }
31print(string.format("a_dict = %s", a_dict))
32
33---@type number[]
34local a_list_of_number = { 1, 2, 3 }
35print(string.format("a_list_of_number = %s", a_list_of_number))
36
37---@type table<string, number>
38local a_dict_of_string_and_number = { a = 1, b = 2, c = 3 }
39print(string.format("a_dict_of_string_and_number = %s", a_dict_of_string_and_number))
40
41--[[----------------------------------------------------------------------------
42标注一个结构体类型的 Table
43--]]----------------------------------------------------------------------------
44---@class Person
45---@field name string The person's name
46---@field dob string Date of birth in string format
47---@field age number The person's age
48---@field occupation? string The person's occupation (optional)
49---@field hobbies? string[] A list of the person's hobbies (optional)
50local person = {
51 name = "John Doe",
52 dob = "1990-01-01",
53 age = 33,
54 occupation = "Engineer",
55 hobbies = { "reading", "swimming" }
56}
57print(string.format("person = %s", person))
58
59---@type Person[]
60local person_list = {
61 {
62 name = "John Doe",
63 dob = "1990-01-01",
64 age = 33,
65 occupation = "Engineer",
66 hobbies = { "reading", "swimming" },
67 wrong_attribute = 123 -- 貌似 IDE 无法发现这里的错误
68 },
69 {
70 name = "Alice",
71 dob = "1990-01-01",
72 age = 33,
73 occupation = "Engineer",
74 hobbies = { "reading", "swimming" }
75 }
76}
77print(string.format("person_list = %s", person_list))
78
79--[[----------------------------------------------------------------------------
80使用 Alias
81
82在上面的例子中我们定义了一个 Person 类型, 之后就可以用 Person[] 来标注一个 Person 类型的数组.
83但是公布时每个类型都是一个 class, 对于不是 class 的任何比较复杂的类型, 我们都可以用一个 alias 来简化.
84例如上面这个 Person 的例子等效于下面这个例子:
85--]]----------------------------------------------------------------------------
86---@alias AnotherPerson
87---@field name string The person's name
88---@field dob string Date of birth in string format
89---@field age number The person's age
90---@field occupation? string The person's occupation (optional)
91---@field hobbies? string[] A list of the person's hobbies (optional)
92
93---@type AnotherPerson[]
94local another_person_list = {
95 {
96 name = "John Doe",
97 dob = "1990-01-01",
98 age = 33,
99 occupation = "Engineer",
100 hobbies = { "reading", "swimming" },
101 wrong_attribute = 123 -- 貌似 IDE 无法发现这里的错误
102 },
103 {
104 name = "Alice",
105 dob = "1990-01-01",
106 age = 33,
107 occupation = "Engineer",
108 hobbies = { "reading", "swimming" }
109 }
110}
111print(string.format("another_person_list = %s", another_person_list))
112
113--[[----------------------------------------------------------------------------
114标注一个函数
115--]]----------------------------------------------------------------------------
116---注: 这是最常见的声明函数的方法
117---@param x number description here
118---@param y number description here
119---@return number description here
120local function add_two_value_v1(x, y)
121 return x + y
122end
123print(string.format("res = %s", add_two_value_v1(1, 2)))
124
125---注: 这种声明方式本质上是创建了个匿名函数并赋值给了一个变量, 不太推荐使用
126---@type fun(x: number, y: number): number
127local add_two_value_v2 = function(x, y)
128 return x + y
129end
130print(string.format("res = %s", add_two_value_v2(1, 2)))
[2]:
import subprocess
subprocess.run(["lua", "type_annotation_1.lua"])
a_number = 1
a_string = Alice
[2]:
CompletedProcess(args=['lua', 'type_annotation_1.lua'], returncode=0)
a_boolean = true
a_list = table: 0x600001e54000
a_dict = table: 0x600001e54080
a_list_of_number = table: 0x600001e54100
a_dict_of_string_and_number = table: 0x600001e54140
person = table: 0x600001e54180
person_list = table: 0x600001e54240
another_person_list = table: 0x600001e54200
res = 3
res = 3
[ ]: