Luau suporta um sistema de tipos gradual através do uso de anotações de tipo e inferência de tipo. Esses tipos são usados para fornecer melhores avisos, erros e sugestões no Editor de Scripts.
Definir um tipo
Use a palavra-chave type para definir seus próprios tipos:
type Vector2 = {x: number, y: number}
Modos de inferência
Existem três modos de inferência de tipo no Luau que podem ser definidos na primeira linha de um Script:
- --!nocheck — Não verificar tipos.
- --!nonstrict — Apenas afirma tipos de variáveis se eles forem explicitamente anotados.
- --!strict — Afirma todos os tipos com base no tipo inferido ou explicitamente anotado.
Os modos --!nonstrict e --!strict controlam quão estrito o verificador de tipos é ao inferir e verificar tipos para variáveis e funções. Qualquer incompatibilidade de tipo em scripts é destacada no Editor de Scripts e apresentada como avisos na janela de Análise de Scripts.
Para definir um modo padrão para todos os scripts que você pode substituir conforme necessário, veja Workspace.LuauTypeCheckMode.
Tipos
Uma anotação de tipo pode ser definida usando o operador : após uma variável local, seguido por uma definição de tipo. Por padrão, no modo nonstrict, todas as variáveis são atribuídas ao tipo any.
local foo: string = "bar"local x: number = 5
Existem quatro tipos primitivos que podem ser usados em uma anotação:
- nil - nenhum valor
- boolean - true ou false
- number - um valor numérico
- string - texto
Dentro do Roblox, todas as classes, tipos de dados e enums têm seus próprios tipos que você pode verificar:
local somePart: Part = Instance.new("Part")local brickColor: BrickColor = somePart.BrickColorlocal material: Enum.Material = somePart.Material
Para tornar um tipo opcional, use um ? no final da anotação:
local foo: string? = nil
Isso permitirá que a variável seja do tipo especificado (neste caso string) ou nil.
Tipos literais
Você também pode converter strings e booleanos em valores literais em vez de usar string e boolean:
local alwaysHelloWorld: "Hello world!" = "Hello world!"alwaysHelloWorld = "Just hello!" -- Erro de tipo: Tipo '"Just hello!"' não pode ser convertido para '"Hello world!"'local alwaysTrue: true = false -- Erro de tipo: Tipo 'false' não pode ser convertido para 'true'
Conversões de tipo
Às vezes, você pode precisar ajudar o verificador de tipo convertendo explicitamente um valor para um tipo diferente com o operador :::
local myNumber = 1local myString: stringmyString = myNumber -- Não OK; erro de conversão de tipomyString = myNumber :: any -- OK; todas as expressões podem ser convertidas para 'any'local myFlag = myNumber :: boolean -- Não OK; os tipos não estão relacionados
Tipagem de função
Considere a seguinte função:
local function add(x, y)
return x + y
end
Esta função adiciona x a y, mas gera erro se um ou ambos forem strings. Luau não sabe que esta função só pode usar números. Para evitar esse tipo de problema, adicione tipos aos parâmetros:
local function add(x: number, y: number)
return x + y
end
Luau agora sabe que a função aceita dois números e gera um aviso se você tentar passar qualquer coisa que não seja um número para a função:
add(5, 10)add(5, "foo") -- Erro de tipo: string não pode ser convertido em number
Para definir um tipo de retorno, coloque um operador : no final da definição da função:
local function add(x: number, y: number): number
Para retornar múltiplos tipos, coloque os tipos entre parênteses:
local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- Erros de tipo
end
Definir um tipo funcional
Um tipo funcional pode ser definido usando a sintaxe (in) -> out. Usando as funções dos exemplos anteriores, os tipos das funções são:
type add = (x: number, y: number) -> numbertype FindSource = (script: BaseScript, pattern: string) -> (string, number)
Tipos de tabela
Luau não tem um tipo table; em vez disso, os tipos de tabela são definidos usando a sintaxe {}. Uma forma de definir tabelas é usando a sintaxe {type}, que define um tipo de lista.
local numbers: {number} = {1, 2, 3, 4, 5}local characterParts: {Instance} = LocalPlayer.Character:GetChildren()
Defina tipos de índice usando {[indexType]: valueType}:
local numberList: {[string]: number} = {Foo = 1,Baz = 10}numberList["bar"] = true -- Erro de tipo: boolean não pode ser convertido em number
As tabelas também podem ter índices de string explícitos definidos em um tipo.
type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- Sempre respeitar o limite de velocidade
end
local taxi: Car = {Speed = 30, Drive = drive}
Variádicos
Aqui está uma função que calcula a soma de uma quantidade arbitrária de números:
local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
end
Como esperado, esta função pode aceitar qualquer valor, e o verificador de tipo não levantará um aviso se você fornecer um tipo inválido, como um string.
print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Tentativa de adicionar string a number
Em vez disso, atribua um tipo ao ..., assim como você atribui qualquer outro tipo:
local function addLotsOfNumbers(...: number)
E agora, a segunda linha gera um erro de tipo.
print(addLotsOfNumbers(1, 2, 3, 4, 5))print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Erro de tipo: string não pode ser convertido em number
No entanto, isso não funciona ao escrever uma definição de tipo funcional:
type addLotsOfNumbers = (...: number) -> number -- Tipo esperado, obtido ':'
Em vez disso, use a sintaxe ...type para definir um tipo variádico.
type addLotsOfNumbers = (...number) -> number
Uniões e interseções
Você pode até definir um tipo como dois ou mais tipos usando uma união ou interseção:
type numberOrString = number | stringtype type1 = {foo: string}type type2 = {bar: number}type type1and2 = type1 & type2 -- {foo: string} & {bar: number}local numString1: numberOrString = true -- Erro de tipolocal numString2: type1and2 = {foo = "hello", bar = 1}
Definir um tipo inferido
Você pode usar a função typeof em uma definição de tipo para tipos inferidos:
type Car = typeof({Speed = 0,Wheels = 4}) --> Car: {Speed: number, Wheels: number}
Uma maneira de usar typeof é definir um tipo de metatable usando setmetatable dentro da função typeof:
type Vector = typeof(setmetatable({}::{x: number,y: number}, {}::{__add: (Vector, Vector|number) -> Vector}))-- Vector + Vector retornaria um tipo Vector
Genéricos
Genéricos são, em um nível básico, parâmetros para tipos. Considere o seguinte objeto State:
local State = {Key = "TimesClicked",Value = 0}
Sem genéricos, o tipo para este objeto seria o seguinte:
type State = {Key: string,Value: number}
No entanto, você pode querer que o tipo para Value seja baseado no valor de entrada, que é onde os genéricos entram:
type GenericType<T> = T
O <T> denota um tipo que pode ser definido como qualquer coisa. A melhor maneira de visualizar isso é como um tipo de substituição.
type List<T> = {T}local Names: List<string> = {"Bob", "Dan", "Mary"} -- O tipo se torna {string}local Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- O tipo se torna {number}
Genéricos também podem ter múltiplas substituições dentro dos colchetes.
type Map<K, V> = {[K]: V}
Para reformular o objeto State de antes para usar um tipo genérico:
type State<T> = {Key: string,Value: T}
Genéricos de Função
Funções também podem usar genéricos. O exemplo de State infere o valor de T dos argumentos de entrada da função.
Para definir uma função genérica, adicione um <> ao nome da função:
local function State<T>(key: string, value: T): State<T>
return {
Key = key,
Value = value
}
end
local Activated = State("Activated", false) -- State<boolean>
local TimesClicked = State("TimesClicked", 0) -- State<number>
Exportações de tipo
Para que um tipo possa ser usado fora de um ModuleScript, use a palavra-chave export:
Módulo de Tipos em ReplicatedStorageexport type Cat = {Name: string,Meow: (Cat) -> ()}
Script Usando o Módulo de Tipos
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()