Luau prend en charge un système de type progressif grâce à l'utilisation d'annotations de type et d'inférence de type.Ces types sont utilisés pour fournir de meilleures alertes, erreurs et suggestions dans l'éditeur de scripts.
Définir un taper
Utilisez le mot-clé type pour définir vos propres types :
type Vector2 = {x: number, y: number}
Modes d'inférence
Il existe trois modes d'inférence de type Luau qui peuvent être définis sur la première ligne d'un Script :
- --!nocheck — Ne pas vérifier les types.
- --!nonstrict — Ne garantit que les types de variables sont explicitement annotés si ils sont explicitement annotés.
- --!strict — Affirme tous les types en se basant sur le taperinféré ou explicitement annoté.
Les modes --!nonstrict et --!strict contrôlent à quel point le vérificateur de type est strict avec l'inférence et la vérification des types pour les variables et les fonctions.Tout type de correspondances dans les scripts est mis en évidence dans l'éditeur de scripts et apparaît comme avertissement dans la fenêtre analyse des scripts .
Les types
Une annotation de type peut être définie en utilisant l'opérateur : après une variable locale, suivie d'une définition de type.Par défaut, en mode nonstrict , toutes les variables sont attribuées le type any .
local foo: string = "bar"local x: number = 5
Il y a quatre types primitifs qui peuvent être utilisés dans une annotation :
- nil - aucune valeur
- boolean - true or false
- number - une valeur numérique
- string - texte
Dans Roblox, toutes les Classes, les types de données et les enums ont leurs propres types que vous pouvez vérifier :
local somePart: Part = Instance.new("Part")local brickColor: BrickColor = somePart.BrickColorlocal material: Enum.Material = somePart.Material
Pour rendre un type facultatif, utilisez un ? à la fin de l'annotation :
local foo: string? = nil
Cela permettra à la variable d'être soit le type spécifié (dans ce cas string ) ou nil .
Types littéraux
Vous pouvez également convertir des chaînes et des booléens en valeurs littérales au lieu d'utiliser string et boolean :
local alwaysHelloWorld: "Hello world!" = "Hello world!"alwaysHelloWorld = "Just hello!" -- Erreur de type : la conversion de 'Just hello!' en 'Hello monde!' n'a pas pu être effectuéelocal alwaysTrue: true = false -- Type error: Type 'false' could not be converted into 'true'
Type de casts
Parfois, vous devrez peut-être aider le vérificateur de type en lançant explicitement une valeur dans un autre type avec l'opérateur :: :
local myNumber = 1local myString: stringmyString = myNumber -- Pas OK ; erreur de conversion de typemyString = myNumber :: any -- OK; toutes les expressions peuvent être mises à 'n'importe quelle'local myFlag = myNumber :: boolean -- Not OK; types are unrelated
Typage de fonction
Considérez la fonction suivante :
local function add(x, y)
return x + y
end
Cette fonction ajoute x à y, mais erre si l'une ou l'autre est une chaîne.Luau ne sait pas que cette fonction ne peut utiliser que des nombres.Pour empêcher cette catégorie de problème, ajoutez des types aux paramètres :
local function add(x: number, y: number)
return x + y
end
Luau sait maintenant que la fonction prend deux nombres et lance une avertissement si vous essayez de passer quelque chose qui n'est pas un nombre dans la fonction :
add(5, 10)add(5, "foo") -- Type error: string could not be converted into number
Pour définir un taperde retour, mettez un opérateur : à la fin de la définition de la fonction :
local function add(x: number, y: number): number
Pour retourner plusieurs types, placez les types entre parenthèses :
local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- Erreurs de type
end
Définir un taperfonctionnel
Un type fonctionnel peut être défini en utilisant la syntaxe (in) -> out. En utilisant les fonctions des exemples précédents, les types des fonctions sont :
type add = (x: number, y: number) -> numbertype FindSource = (script: BaseScript, pattern: string) -> (string, number)
Types de table
Luau n'a pas de tapertable ; à la place, les types de table sont définis en utilisant la syntaxe {}.Une façon de définir des tables est d'utiliser la syntaxe {type}, qui définit un taperde liste.
local numbers: {number} = {1, 2, 3, 4, 5}local characterParts: {Instance} = LocalPlayer.Character:GetChildren()
Définir les types d'index en utilisant {[indexType]: valueType} :
local numberList: {[string]: number} = {Foo = 1,Baz = 10}numberList["bar"] = true -- Type error: boolean can't convert to number
Les tables peuvent également avoir des index de chaîne explicites définis dans un taper.
type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- Allez toujours à la limite de vitesse
end
local taxi: Car = {Speed = 30, Drive = drive}
Variadiques
Voici une fonction qui calcule la somme d'un nombre arbitraire :
local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
end
Comme prévu, cette fonction peut prendre n'importe quelle valeur, et le vérificateur de type ne lèvera pas d'avertissement si vous fournissez un taperinvalide, comme un string .
print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Attempt to add string to number
Au lieu de cela, attribuez un type au ..., tout comme la façon dont vous attribuez tout autre taper:
local function addLotsOfNumbers(...: number)
Et maintenant, la deuxième ligne soulève une erreur de type.
print(addLotsOfNumbers(1, 2, 3, 4, 5))print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Erreur de type : la chaîne ne pouvait pas être convertie en nombre
Cependant, cela ne fonctionne pas lors de l'écriture d'une définition de type fonctionnelle :
type addLotsOfNumbers = (...: number) -> number -- Expected type, got ':'
Au lieu de cela, utilisez la syntaxe ...type pour définir un tapervariatique.
type addLotsOfNumbers = (...number) -> number
Unions et intersections
Vous pouvez même définir un type comme deux types ou plus en utilisant une union ou une intersection :
type numberOrString = number | stringtype type1 = {foo: string}type type2 = {bar: number}type type1and2 = type1 & type2 -- {foo: chaîne} & {bar: number}local numString1: numberOrString = true -- Erreur de typelocal numString2: type1and2 = {foo = "hello", bar = 1}
Définir un taperinféré
Vous pouvez utiliser la fonction typeof dans une définition de type pour les types inférés :
type Car = typeof({Speed = 0,Wheels = 4}) --> Car: {Speed: number, Wheels: number}
Une façon d'utiliser typeof est de définir un type métatable en utilisant setmetatable à l'intérieur de la fonction typeof :
type Vector = typeof(setmetatable({}::{x: number,y: number}, {}::{__add: (Vector, Vector|number) -> Vector}))-- Vector + Vector would return a Vector type
Génériques
Les génériques sont des paramètres de niveau basique pour les types. Considérez l'objet suivant State :
local State = {Key = "TimesClicked",Value = 0}
Sans génériques, le type pour cet objet serait le suivant :
type State = {Key: string,Value: number}
Cependant, vous voudrez peut-être que le type pour Value soit basé sur la valeur entrante, où les génériques entrent en jeu :
type GenericType<T> = T
Le <T> désigne un type qui peut être défini sur n'importe quoi. La meilleure façon de visualiser cela est comme un taperde substitution.
type List<T> = {T}local Names: List<string> = {"Bob", "Dan", "Mary"} -- Le type devient {chaîne}local Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- Type becomes {number}
Les génériques peuvent également avoir plusieurs substitutions à l'intérieur des parenthèses.
type Map<K, V> = {[K]: V}
Pour réutiliser l'objet State d'avant pour utiliser un tapergénérique :
type State<T> = {Key: string,Value: T}
Génériques de fonction
Les fonctions peuvent également utiliser des génériques. L'exemple State infère la valeur de T de la fonction à partir des arguments entrants.
Pour définir une fonction générique, ajoutez un <> à le nom de la fonction :
local function State<T>(key: string, value: T): State<T>
return {
Key = key,
Value = value
}
end
local Activated = State("Activated", false) -- État<booléen>
local TimesClicked = State("TimesClicked", 0) -- State<number>
Exports de type
Pour qu'un type puisse être utilisé en dehors d'un ModuleScript, utilisez le mot-clé export :
Module de types dans ReplicatedStorage
export type Cat = {Name: string,Meow: (Cat) -> ()}
Script utilisant le module de types
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()