Luau supporta un sistema di tipi graduali attraverso l'uso di annotazioni di tipo e inferenza di tipo. Questi tipi vengono utilizzati per fornire migliori avvisi, errori e suggerimenti nell' Editor di Script.
Definire un tipo
Usa la parola chiave type per definire i tuoi tipi:
type Vector2 = {x: number, y: number}
Modi di inferenza
Ci sono tre modalità di inferenza dei tipi in Luau che possono essere impostate sulla prima riga di un Script:
- --!nocheck — Non controllare i tipi.
- --!nonstrict — Asserisce i tipi delle variabili solo se sono esplicitamente annotati.
- --!strict — Asserisce tutti i tipi sulla base del tipo inferito o esplicitamente annotato.
Le modalità --!nonstrict e --!strict controllano quanto sia rigoroso il verificatore di tipi con l'inferenza e il controllo dei tipi per variabili e funzioni. Qualsiasi incompatibilità di tipo negli script è evidenziata nell' Editor di Script e mostrata come avvisi nella finestra di Analisi degli Script.
Per impostare una modalità predefinita per tutti gli script che puoi sovrascrivere secondo necessità, vedere Workspace.LuauTypeCheckMode.
Tipi
Un'annotazione di tipo può essere definita utilizzando l'operatore : dopo una variabile locale, seguita da una definizione di tipo. Per impostazione predefinita, in modalità nonstrict, tutte le variabili vengono assegnate al tipo any.
local foo: string = "bar"local x: number = 5
Ci sono quattro tipi primitivi che possono essere usati in un'annotazione:
- nil - nessun valore
- boolean - true o false
- number - un valore numerico
- string - testo
All'interno di Roblox, tutte le classi, i tipi di dati e gli enum hanno i propri tipi contro cui puoi controllare:
local somePart: Part = Instance.new("Part")local brickColor: BrickColor = somePart.BrickColorlocal material: Enum.Material = somePart.Material
Per rendere un tipo opzionale, usa un ? alla fine dell'annotazione:
local foo: string? = nil
Questo permetterà alla variabile di essere sia del tipo specificato (in questo caso string) che nil.
Tipi letterali
Puoi anche convertire stringhe e booleani in valori letterali invece di usare string e boolean:
local alwaysHelloWorld: "Hello world!" = "Hello world!"alwaysHelloWorld = "Just hello!" -- Errore di tipo: Tipo '"Just hello!"' non può essere convertito in '"Hello world!"'local alwaysTrue: true = false -- Errore di tipo: Tipo 'false' non può essere convertito in 'true'
Conversioni di tipo
A volte, potresti dover assistere il verificatore di tipi esplicitamente convertendo un valore in un tipo diverso con l'operatore :::
local myNumber = 1local myString: stringmyString = myNumber -- Non OK; errore di conversione di tipomyString = myNumber :: any -- OK; tutte le espressioni possono essere convertite in 'any'local myFlag = myNumber :: boolean -- Non OK; i tipi sono non correlati
Tipizzazione delle funzioni
Considera la seguente funzione:
local function add(x, y)
return x + y
end
Questa funzione somma x a y, ma genera un errore se uno o entrambi sono stringhe. Luau non sa che questa funzione può usare solo numeri. Per prevenire questa categoria di problemi, aggiungi i tipi ai parametri:
local function add(x: number, y: number)
return x + y
end
Luau ora sa che la funzione accetta due numeri e genera un avviso se provi a passare qualsiasi cosa che non sia un numero nella funzione:
add(5, 10)add(5, "foo") -- Errore di tipo: string non può essere convertito in numero
Per definire un tipo di ritorno, metti un'operatore : alla fine della definizione della funzione:
local function add(x: number, y: number): number
Per restituire più tipi, metti i tipi tra parentesi:
local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- Errori di tipo
end
Definire un tipo funzionale
Un tipo funzionale può essere definito utilizzando la sintassi (in) -> out. Usando le funzioni degli esempi precedenti, i tipi delle funzioni sono:
type add = (x: number, y: number) -> numbertype FindSource = (script: BaseScript, pattern: string) -> (string, number)
Tipi di tabella
Luau non ha un tipo table; invece, i tipi di tabella vengono definiti utilizzando la sintassi {}. Un modo per definire le tabelle è utilizzare la sintassi {type}, che definisce un tipo di lista.
local numbers: {number} = {1, 2, 3, 4, 5}local characterParts: {Instance} = LocalPlayer.Character:GetChildren()
Definisci i tipi di indice usando {[indexType]: valueType}:
local numberList: {[string]: number} = {Foo = 1,Baz = 10}numberList["bar"] = true -- Errore di tipo: boolean non può essere convertito in numero
Le tabelle possono anche avere indici stringa espliciti definiti in un tipo.
type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- Andare sempre al limite di velocità
end
local taxi: Car = {Speed = 30, Drive = drive}
Variadici
Ecco una funzione che calcola la somma di un numero arbitrario di numeri:
local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
end
Come previsto, questa funzione può prendere qualsiasi valore e il verificatore di tipi non genererà un avviso se fornisci un tipo non valido, come una string.
print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Tentativo di aggiungere stringa a numero
Invece, assegna un tipo a ..., proprio come fai con qualsiasi altro tipo:
local function addLotsOfNumbers(...: number)
E ora, la seconda riga genera un errore di tipo.
print(addLotsOfNumbers(1, 2, 3, 4, 5))print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Errore di tipo: string non può essere convertito in numero
Tuttavia, questo non funziona quando scrivi una definizione di tipo funzionale:
type addLotsOfNumbers = (...: number) -> number -- Tipo previsto, ottenuto ':'
Invece, usa la sintassi ...tipo per definire un tipo variadico.
type addLotsOfNumbers = (...number) -> number
Unioni e intersezioni
Puoi anche definire un tipo come due o più tipi usando un'unione o un'intersezione:
type numberOrString = number | stringtype type1 = {foo: string}type type2 = {bar: number}type type1and2 = type1 & type2 -- {foo: string} & {bar: number}local numString1: numberOrString = true -- Errore di tipolocal numString2: type1and2 = {foo = "hello", bar = 1}
Definire un tipo inferito
Puoi usare la funzione typeof in una definizione di tipo per i tipi inferiti:
type Car = typeof({Speed = 0,Wheels = 4}) --> Car: {Speed: number, Wheels: number}
Un modo per usare typeof è definire un tipo di metatabella usando setmetatable all'interno della funzione typeof:
type Vector = typeof(setmetatable({}::{x: number,y: number}, {}::{__add: (Vector, Vector|number) -> Vector}))-- Vector + Vector restituirebbe un tipo Vector
Generics
I generics sono, a un livello di base, parametri per i tipi. Considera il seguente oggetto State:
local State = {Key = "TimesClicked",Value = 0}
Senza generics, il tipo per questo oggetto sarebbe il seguente:
type State = {Key: string,Value: number}
Tuttavia, potresti voler che il tipo per Value sia basato sul valore in ingresso, ed è qui che entrano in gioco i generics:
type GenericType<T> = T
Il <T> denota un tipo che può essere impostato su qualsiasi cosa. Il modo migliore per visualizzare questo è come un tipo di sostituzione.
type List<T> = {T}local Names: List<string> = {"Bob", "Dan", "Mary"} -- Il tipo diventa {string}local Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- Il tipo diventa {number}
I generics possono anche avere più sostituzioni all'interno delle parentesi.
type Map<K, V> = {[K]: V}
Per rielaborare l'oggetto State di cui sopra per utilizzare un tipo generico:
type State<T> = {Key: string,Value: T}
Funzioni generiche
Le funzioni possono anche utilizzare i generics. L'esempio State infers il valore di T dagli argomenti in ingresso della funzione.
Per definire una funzione generica, aggiungi un <> al nome della funzione:
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>
Esportazioni di tipo
Per fare in modo che un tipo possa essere utilizzato al di fuori di un ModuleScript, usa la parola chiave export:
Modulo di Tipi in ReplicatedStorageexport type Cat = {Name: string,Meow: (Cat) -> ()}
Script che utilizza il Modulo di Tipi
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()