Luau unterstützt ein schrittweises Typsystem durch die Verwendung von Typannotationen und Typinferenz. Diese Typen werden verwendet, um bessere Warnungen, Fehler und Vorschläge im Skript-Editor bereitzustellen.
Definiere einen Typ
Verwende das Schlüsselwort type, um deine eigenen Typen zu definieren:
type Vector2 = {x: number, y: number}Inferenzmodi
Es gibt drei Luau-Typinferenzmodi, die in der ersten Zeile eines Script festgelegt werden können:
- --!nocheck — Überprüfe keine Typen.
- --!nonstrict — Stellt nur sicher, dass Variablentypen angegeben werden, wenn sie explizit annotiert sind.
- --!strict — Stellt alle Typen basierend auf dem inferierten oder explizit annotierten Typ sicher.
Die Modi --!nonstrict und --!strict kontrollieren, wie streng der Typprüfer bei der Inferenz und Überprüfung von Typen für Variablen und Funktionen ist. Alle Typmissergebnisse in Skripten werden im Skript-Editor hervorgehoben und als Warnungen im Fenster Skriptanalyse angezeigt.
Um einen Standardmodus für alle Skripte festzulegen, den du nach Bedarf überschreiben kannst, siehe Workspace.LuauTypeCheckMode.
Typen
Eine Typannotation kann unter Verwendung des :-Operators nach einer lokalen Variable definiert werden, gefolgt von einer Typdefinition. Standardmäßig wird im nonstrict-Modus allen Variablen der Typ any zugewiesen.
local foo: string = "bar"
local x: number = 5Es gibt vier primitive Typen, die in einer Annotation verwendet werden können:
- nil - kein Wert
- boolean - true oder false
- number - ein numerischer Wert
- string - Text
Innerhalb von Roblox haben alle Klassen, Datentypen und Enums ihre eigenen Typen, gegen die du überprüfen kannst:
local somePart: Part = Instance.new("Part")
local brickColor: BrickColor = somePart.BrickColor
local material: Enum.Material = somePart.MaterialUm einen Typ optional zu machen, verwende ein ? am Ende der Annotation:
local foo: string? = nilDies erlaubt es der Variablen, entweder den angegebenen Typ (in diesem Fall string) oder nil zu sein.
Literale Typen
Du kannst auch Zeichenfolgen und Booleans in literale Werte umwandeln, anstatt string und boolean zu verwenden:
local alwaysHelloWorld: "Hello world!" = "Hello world!"
alwaysHelloWorld = "Just hello!" -- Typfehler: Typ '"Just hello!"' konnte nicht in '"Hello world!"' umgewandelt werden
local alwaysTrue: true = false -- Typfehler: Typ 'false' konnte nicht in 'true' umgewandelt werdenTypumwandlungen
Manchmal musst du den Typprüfer unterstützen, indem du einen Wert mit dem ::-Operator explizit in einen anderen Typ umwandelst:
local myNumber = 1
local myString: string
myString = myNumber -- Nicht OK; Typumwandlungsfehler
myString = myNumber :: any -- OK; alle Ausdrücke können in 'any' umgewandelt werden
local myFlag = myNumber :: boolean -- Nicht OK; Typen sind nicht verwandtFunktionstypen
Betrachte die folgende Funktion:
local function add(x, y)
return x + y
endDiese Funktion addiert x zu y, gibt jedoch einen Fehler zurück, wenn einer oder beide davon eine Zeichenfolge sind. Luau weiß nicht, dass diese Funktion nur Zahlen verwenden kann. Um diese Art von Problem zu verhindern, füge Typen zu den Parametern hinzu:
local function add(x: number, y: number)
return x + y
endLuau weiß nun, dass die Funktion zwei Zahlen erwartet, und gibt eine Warnung aus, wenn du versuchst, etwas, das keine Zahl ist, in die Funktion zu übergeben:
add(5, 10)
add(5, "foo") -- Typfehler: Zeichenfolge konnte nicht in Zahl umgewandelt werdenUm einen Rückgabewert zu definieren, setze einen :-Operator am Ende der Funktionsdefinition:
local function add(x: number, y: number): numberUm mehrere Typen zurückzugeben, setze die Typen in Klammern:
local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- Typfehler
endDefiniere einen funktionalen Typ
Ein funktionaler Typ kann mit der Syntax (in) -> out definiert werden. Unter Verwendung der Funktionen aus den vorherigen Beispielen lauten die Typen der Funktionen:
type add = (x: number, y: number) -> number
type FindSource = (script: BaseScript, pattern: string) -> (string, number)Tabellentypen
Luau hat keinen table-Typ; stattdessen werden Tabellentypen mit der {}-Syntax definiert. Eine Möglichkeit, Tabellen zu definieren, besteht darin, die {type}-Syntax zu verwenden, die einen Listentyp definiert.
local numbers: {number} = {1, 2, 3, 4, 5}
local characterParts: {Instance} = LocalPlayer.Character:GetChildren()Definiere Indextypen mit {[indexType]: valueType}:
local numberList: {[string]: number} = {
Foo = 1,
Baz = 10
}
numberList["bar"] = true -- Typfehler: boolean kann nicht in Zahl umgewandelt werdenTabellen können auch explizite Zeichenfolgenindizes in einem Typ definiert haben.
type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- Immer die Geschwindigkeitsgrenze einhalten
end
local taxi: Car = {Speed = 30, Drive = drive}Variadische Argumente
Hier ist eine Funktion, die die Summe einer beliebigen Anzahl von Zahlen berechnet:
local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
endWie erwartet kann diese Funktion jeden Wert annehmen, und der Typprüfer gibt keine Warnung aus, wenn du einen ungültigen Typ (wie eine string) bereitstellst.
print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15
print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Versuch, einen String zu einer Zahl hinzuzufügenStattdessen weise dem ... einen Typ zu, genau wie du jedem anderen Typ eine Zuweisung machst:
local function addLotsOfNumbers(...: number)Und jetzt wird die zweite Zeile einen Typfehler auslösen.
print(addLotsOfNumbers(1, 2, 3, 4, 5))
print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- Typfehler: string konnte nicht in Zahl umgewandelt werdenDies funktioniert jedoch nicht, wenn du eine funktionale Typdefinition schreibst:
type addLotsOfNumbers = (...: number) -> number -- Erwarteter Typ, erhalten: ':'Verwende stattdessen die Syntax ...type, um einen variadischen Typ zu definieren.
type addLotsOfNumbers = (...number) -> numberVereinigungen und Schnittmengen
Du kannst sogar einen Typ als zwei oder mehr Typen mithilfe einer Vereinigung oder Schnittmenge definieren:
type numberOrString = number | string
type type1 = {foo: string}
type type2 = {bar: number}
type type1and2 = type1 & type2 -- {foo: string} & {bar: number}
local numString1: numberOrString = true -- Typfehler
local numString2: type1and2 = {foo = "hello", bar = 1}Definiere einen inferierten Typ
Du kannst die Funktion typeof in einer Typdefinition für inferierte Typen verwenden:
type Car = typeof({
Speed = 0,
Wheels = 4
}) --> Car: {Speed: number, Wheels: number}Eine Möglichkeit, typeof zu verwenden, besteht darin, einen Metatyp mithilfe von setmetatable innerhalb der typeof-Funktion zu definieren:
type Vector = typeof(setmetatable({}::{
x: number,
y: number
}, {}::{
__add: (Vector, Vector|number) -> Vector
}))
-- Vector + Vector würde einen Vektor-Typ zurückgebenGenerika
Generika sind auf einer grundlegenden Ebene Parameter für Typen. Betrachte das folgende State-Objekt:
local State = {
Key = "TimesClicked",
Value = 0
}Ohne Generika würde der Typ für dieses Objekt wie folgt aussehen:
type State = {
Key: string,
Value: number
}Wenn du jedoch möchtest, dass der Typ für Value auf dem eingehenden Wert basiert, kommt hier die Generik ins Spiel:
type GenericType<T> = TDas <T> bezeichnet einen Typ, der auf alles gesetzt werden kann. Am besten kann man sich das als einen Substitutionstyp vorstellen.
type List<T> = {T}
local Names: List<string> = {"Bob", "Dan", "Mary"} -- Typ wird {string}
local Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- Typ wird {number}Generika können auch mehrere Substitutionen innerhalb der Klammern haben.
type Map<K, V> = {[K]: V}Um das State-Objekt von früher so umzuarbeiten, dass es einen generischen Typ verwendet:
type State<T> = {
Key: string,
Value: T
}Funktionsgenerika
Funktionen können auch Generika verwenden. Das State-Beispiel leitet den Wert von T von den eingehenden Argumenten der Funktion ab.
Um eine generische Funktion zu definieren, füge <> zum Funktionsnamen hinzu:
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>Typ-Exports
Um es so zu machen, dass ein Typ außerhalb eines ModuleScript verwendet werden kann, verwende das Schlüsselwort export:
export type Cat = {
Name: string,
Meow: (Cat) -> ()
}local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Types = require(ReplicatedStorage.Types)
local newCat: Types.Cat = {
Name = "metatablecat",
Meow = function(self)
print(`{self.Name} sagte miau`)
end
}
newCat:Meow()