Vérification de type

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

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.BrickColor
local 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ée
local 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 = 1
local myString: string
myString = myNumber -- Pas OK ; erreur de conversion de type
myString = 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) -> number
type 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)) -- 15
print(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 | string
type type1 = {foo: string}
type type2 = {bar: number}
type type1and2 = type1 & type2 -- {foo: chaîne} & {bar: number}
local numString1: numberOrString = true -- Erreur de type
local 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()