类型检查

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

Luau 通过使用类型注释和类型推理来支持逐渐类型系统。这些类型用于在 脚本编辑器 中提供更好的警告、错误和建议。

定义一个类输入

使用 type 关键字来定义自己的类型:


type Vector2 = {x: number, y: number}

推理模式

有三种 Luau 类型推理模式可以在 Script 的第一行上设置:

  • --!nocheck — 不要检查类型。
  • --!nonstrict — 仅声明变量类型,如果它们被明确注释。
  • --!strict — 确认所有类型基于导出或明确标记的输入。

--!nonstrict--!strict 模式控制类型检查器是否严格检查变量和函数的类型以进行推理和检查。脚本中的任何类型匹配失败都会被标记在 脚本编辑器 中,并在 脚本分析窗口 中显示为警告。

类型

类型注释可以使用 : 运营符后定义一个本地变量,然后定义一个类型定义。默认情况下,在 nonstrict 模式下,所有变量都被分配类型 any


local foo: string = "bar"
local x: number = 5

在注释中可以使用四种原始类型:

  • nil - 无值
  • boolean - true or false
  • number - 一个数值
  • string - 文本

在 Roblox 内,所有类别、数据类型和枚都有自己可以检查的类型:


local somePart: Part = Instance.new("Part")
local brickColor: BrickColor = somePart.BrickColor
local material: Enum.Material = somePart.Material

要使类型可选,请在注释末尾使用 ?


local foo: string? = nil

这将允许变量是指定类型(在这种情况下是 string )或 nil

字面类型

您还可以将字符串和布尔转换为文本值,而不使用 stringboolean


local alwaysHelloWorld: "Hello world!" = "Hello world!"
alwaysHelloWorld = "Just hello!" -- 类型错误:类型 “’只是你好!’” 无法转换为 “’你好世界!’”
local alwaysTrue: true = false -- Type error: Type 'false' could not be converted into 'true'

类型投射

有时,您可能需要通过使用 :: 运营符明确将值转换为不同类型来帮助类型检查器:


local myNumber = 1
local myString: string
myString = myNumber -- 不支持;输入转换错误
myString = myNumber :: any -- 好的;所有语句都可以投射到“任何”
local myFlag = myNumber :: boolean -- Not OK; types are unrelated

函数类型

考虑以下函数:


local function add(x, y)
return x + y
end

这个函数添加 xy ,但如果其中一个或两个是字符串,将发生错误。Luau不知道这个函数只能使用数字。为了防止这一类问题,将类型添加到参数中:


local function add(x: number, y: number)
return x + y
end

Luau现在知道,函数接受两个数字,如果你试图将不是数字的任何东西传递到函数中,将触发警告:


add(5, 10)
add(5, "foo") -- Type error: string could not be converted into number

要定义返回类输入,在函数定义的最后放置一个 : 运营符:


local function add(x: number, y: number): number

要返回多个类型,请将类型放在括号中:


local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- 类型错误
end

定义一个功能类输入

功能类型可以通过使用语法 (in) -> out 来定义。使用前面的示例中的函数,函数类型是:


type add = (x: number, y: number) -> number
type FindSource = (script: BaseScript, pattern: string) -> (string, number)

表类型

Luau没有 table 输入;相反,表类型使用 {} 语法进行定义。定义表的一个方法是使用 {type} 语法,该语法定义了列表输入。


local numbers: {number} = {1, 2, 3, 4, 5}
local characterParts: {Instance} = LocalPlayer.Character:GetChildren()

使用 {[indexType]: valueType} 定义索引类型:


local numberList: {[string]: number} = {
Foo = 1,
Baz = 10
}
numberList["bar"] = true -- Type error: boolean can't convert to number

表也可以在类输入中定义明确的字符串索引。


type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- 总是遵循速度限制
end
local taxi: Car = {Speed = 30, Drive = drive}

变体

这里是一个计算随意数量数字的总和的函数:


local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
end

如期待的那样,这个函数可以接受任何值,并且类型检查器不会在你提供无效输入,例如 string 时提出警告。


print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15
print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Attempt to add string to number

相反,将类型分配给 ... , 就像你分配任何其他输入一样:


local function addLotsOfNumbers(...: number)

现在,第二行提出了类型错误。


print(addLotsOfNumbers(1, 2, 3, 4, 5))
print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- 类型错误:字符串无法转换为数字

但是,写功能类型定义时不起作用:


type addLotsOfNumbers = (...: number) -> number -- Expected type, got ':'

而是使用语法 ...type 来定义变量类输入。


type addLotsOfNumbers = (...number) -> number

联盟和交叉点

您甚至可以使用联盟或交集定义类型为两个或更多类型:


type numberOrString = number | string
type type1 = {foo: string}
type type2 = {bar: number}
type type1and2 = type1 & type2 -- {foo: 字符串tring} 和 {bar: number}
local numString1: numberOrString = true -- 类型错误
local numString2: type1and2 = {foo = "hello", bar = 1}

定义一个派生类输入

您可以在类型定义中使用 typeof 函数来检查类型:


type Car = typeof({
Speed = 0,
Wheels = 4
}) --> Car: {Speed: number, Wheels: number}

使用 typeof 的一个方法是在 setmetatable 函数内使用 typeof 定义一个可转换类型:


type Vector = typeof(setmetatable({}::{
x: number,
y: number
}, {}::{
__add: (Vector, Vector|number) -> Vector
}))
-- Vector + Vector would return a Vector type

通用类

通用型是类型的基本参数级别。考虑以下 State 对象:


local State = {
Key = "TimesClicked",
Value = 0
}

没有类型化,这个对象的类型将如下:


type State = {
Key: string,
Value: number
}

然而,你可能希望类型为 Value 基于收到的值,这是 génériques 进来的地方:


type GenericType<T> = T

<T> 表示可以设置为任何东西的类型。最好的方法是用作替换输入来显示这一点。


type List<T> = {T}
local Names: List<string> = {"Bob", "Dan", "Mary"} -- 类型变为 {字符串tring}
local Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- Type becomes {number}

通用也可以在括号内有多个替换。


type Map<K, V> = {[K]: V}

要重新处理 State 对象从以前使用通用类输入:


type State<T> = {
Key: string,
Value: T
}

函数通用型

函数也可以使用类型。 State 示例从函数的输入参数中推导 T 的值。

要定义通用函数,请将 <> 添加到函数名称:


local function State<T>(key: string, value: T): State<T>
return {
Key = key,
Value = value
}
end
local Activated = State("Activated", false) -- 状态<boolean>
local TimesClicked = State("TimesClicked", 0) -- State<number>

类型输出

要使它可以在 ModuleScript 之外使用,使用 export 关键字:

复制存储中的类型模块

export type Cat = {
Name: string,
Meow: (Cat) -> ()
}
使用类型模块的脚本

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Types = require(ReplicatedStorage.Types)
local newCat: Types.Cat = {
Name = "metatablecat",
Meow = function(self)
print(`{self.Name} said meow`)
end
}
newCat:Meow()