Luau soporta un sistema de tipos gradual mediante el uso de anotaciones de tipo e inferencia de tipo. Estos tipos se utilizan para proporcionar mejores advertencias, errores y sugerencias en el Editor de Scripts.
Definir un tipo
Utiliza la palabra clave type para definir tus propios tipos:
type Vector2 = {x: number, y: number}
Modos de inferencia
Hay tres modos de inferencia de tipo en Luau que se pueden establecer en la primera línea de un Script:
- --!nocheck — No verificar tipos.
- --!nonstrict — Solo afirma los tipos de las variables si están anotados explícitamente.
- --!strict — Afirma todos los tipos en base al tipo inferido o anotado explícitamente.
Los modos --!nonstrict y --!strict controlan cuán estricto es el verificador de tipos al inferir y verificar tipos para variables y funciones. Cualquier desajuste de tipo en los scripts se resalta en el Editor de Scripts y se presenta como advertencias en la ventana de Análisis de Scripts.
Para establecer un modo predeterminado para todos los scripts que puedas anular según sea necesario, consulta Workspace.LuauTypeCheckMode.
Tipos
Se puede definir una anotación de tipo utilizando el operador : después de una variable local, seguido de una definición de tipo. Por defecto, en modo nonstrict, todas las variables se asignan el tipo any.
local foo: string = "bar"local x: number = 5
Hay cuatro tipos primitivos que se pueden usar en una anotación:
- nil - sin valor
- boolean - true o false
- number - un valor numérico
- string - texto
Dentro de Roblox, todas las clases, tipos de datos y enumeraciones tienen sus propios tipos contra los cuales puedes verificar:
local somePart: Part = Instance.new("Part")local brickColor: BrickColor = somePart.BrickColorlocal material: Enum.Material = somePart.Material
Para hacer que un tipo sea opcional, usa un ? al final de la anotación:
local foo: string? = nil
Esto permitirá que la variable sea del tipo especificado (en este caso string) o nil.
Tipos literales
También puedes convertir cadenas y booleanos a valores literales en lugar de usar string y boolean:
local alwaysHelloWorld: "Hello world!" = "Hello world!"alwaysHelloWorld = "Just hello!" -- Error de tipo: El tipo '"Just hello!"' no se pudo convertir en '"Hello world!"'local alwaysTrue: true = false -- Error de tipo: El tipo 'false' no se pudo convertir en 'true'
Conversiones de tipo
A veces, es posible que debas ayudar al verificador de tipos convirtiendo explícitamente un valor a un tipo diferente con el operador :::
local myNumber = 1local myString: stringmyString = myNumber -- No está bien; error de conversión de tipomyString = myNumber :: any -- OK; todas las expresiones pueden convertirse a 'any'local myFlag = myNumber :: boolean -- No está bien; los tipos no están relacionados
Tipado de funciones
Considera la siguiente función:
local function add(x, y)
return x + y
end
Esta función suma x a y, pero lanza errores si uno o ambos son una cadena. Luau no sabe que esta función solo puede usar números. Para prevenir este tipo de problemas, añade tipos a los parámetros:
local function add(x: number, y: number)
return x + y
end
Luau ahora sabe que la función toma dos números y lanza una advertencia si intentas pasar algo que no sea un número a la función:
add(5, 10)add(5, "foo") -- Error de tipo: la cadena no se pudo convertir en número
Para definir un tipo de retorno, coloca un operador : al final de la definición de la función:
local function add(x: number, y: number): number
Para retornar múltiples tipos, coloca los tipos entre paréntesis:
local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- Errores de tipo
end
Definir un tipo funcional
Un tipo funcional se puede definir utilizando la sintaxis (in) -> out. Usando las funciones de los ejemplos anteriores, los tipos de las funciones son:
type add = (x: number, y: number) -> numbertype FindSource = (script: BaseScript, pattern: string) -> (string, number)
Tipos de tabla
Luau no tiene un tipo table; en su lugar, los tipos de tabla se definen utilizando la sintaxis {}. Una forma de definir tablas es utilizando la sintaxis {type}, que define un tipo de lista.
local numbers: {number} = {1, 2, 3, 4, 5}local characterParts: {Instance} = LocalPlayer.Character:GetChildren()
Define tipos de índice utilizando {[indexType]: valueType}:
local numberList: {[string]: number} = {Foo = 1,Baz = 10}numberList["bar"] = true -- Error de tipo: boolean no puede convertir a número
Las tablas también pueden tener índices de cadena explícitos definidos en un tipo.
type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- Siempre respetar el límite de velocidad
end
local taxi: Car = {Speed = 30, Drive = drive}
Variádicos
Aquí hay una función que calcula la suma de una cantidad arbitraria de números:
local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
end
Como era de esperar, esta función puede tomar cualquier valor, y el verificador de tipos no lanzará una advertencia si proporcionas un tipo inválido, como una string.
print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Intento de sumar una cadena a un número
En su lugar, asigna un tipo a los ..., así como asignas cualquier otro tipo:
local function addLotsOfNumbers(...: number)
Y ahora, la segunda línea genera un error de tipo.
print(addLotsOfNumbers(1, 2, 3, 4, 5))print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Error de tipo: la cadena no se pudo convertir en número
Sin embargo, esto no funciona al escribir una definición de tipo funcional:
type addLotsOfNumbers = (...: number) -> number -- Tipo esperado, obtuve ':'
En su lugar, usa la sintaxis ...type para definir un tipo variádico.
type addLotsOfNumbers = (...number) -> number
Uniones e intersecciones
Incluso puedes definir un tipo como dos o más tipos usando una unión o intersección:
type numberOrString = number | stringtype type1 = {foo: string}type type2 = {bar: number}type type1and2 = type1 & type2 -- {foo: string} & {bar: number}local numString1: numberOrString = true -- Error de tipolocal numString2: type1and2 = {foo = "hello", bar = 1}
Definir un tipo inferido
Puedes usar la función typeof en una definición de tipo para tipos inferidos:
type Car = typeof({Speed = 0,Wheels = 4}) --> Car: {Speed: number, Wheels: number}
Una forma de usar typeof es definir un tipo de metatabla utilizando setmetatable dentro de la función typeof:
type Vector = typeof(setmetatable({}::{x: number,y: number}, {}::{__add: (Vector, Vector|number) -> Vector}))-- Vector + Vector devolvería un tipo Vector
Genéricos
Los genéricos son, a un nivel básico, parámetros para tipos. Considera el siguiente objeto State:
local State = {Key = "TimesClicked",Value = 0}
Sin genéricos, el tipo para este objeto sería el siguiente:
type State = {Key: string,Value: number}
Sin embargo, puede que desees que el tipo para Value se base en el valor entrante, que es donde entran los genéricos:
type GenericType<T> = T
El <T> denota un tipo que puede ser configurado a cualquier cosa. La mejor manera de visualizar esto es como un tipo de sustitución.
type List<T> = {T}local Names: List<string> = {"Bob", "Dan", "Mary"} -- El tipo se convierte en {string}local Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- El tipo se convierte en {number}
Los genéricos también pueden tener múltiples sustituciones dentro de los corchetes.
type Map<K, V> = {[K]: V}
Para reformular el objeto State de antes para usar un tipo genérico:
type State<T> = {Key: string,Value: T}
Genéricos de función
Las funciones también pueden usar genéricos. El ejemplo de State infiere el valor de T a partir de los argumentos entrantes de la función.
Para definir una función genérica, agrega un <> al nombre de la función:
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>
Exportaciones de tipos
Para hacer que un tipo pueda ser utilizado fuera de un ModuleScript, utiliza la palabra clave export:
Módulo de Tipos en ReplicatedStorageexport type Cat = {Name: string,Meow: (Cat) -> ()}
Script Usando el 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} dijo miau`)
end
}
newCat:Meow()