Luau は、型アノテーションと型推論を用いた段階的型システムをサポートしています。これらの型は、スクリプトエディターでより良い警告、エラー、および提案を提供するために使用されます。
型を定義する
type キーワードを使用して独自の型を定義します。
type Vector2 = {x: number, y: number}
推論モード
Script の最初の行で設定できる Luau の型推論モードは 3 つあります。
- --!nocheck — 型をチェックしない。
- --!nonstrict — 明示的にアノテートされている場合にのみ変数の型をアサートする。
- --!strict — 推論された型または明示的にアノテートされた型に基づいてすべての型をアサートする。
--!nonstrict と --!strict モードは、変数や関数の型推論と型チェックにおいて型チェッカーがどれだけ厳密であるかを制御します。スクリプト内の型の不一致は、スクリプトエディターで強調表示され、スクリプト分析ウィンドウで警告として表示されます。
必要に応じてオーバーライド可能なすべてのスクリプトのデフォルトモードを設定するには、Workspace.LuauTypeCheckMode を参照してください。
型
型アノテーションは、ローカル変数の後に : 演算子を使用して定義され、その後に型定義が続きます。デフォルトでは、nonstrict モードではすべての変数に型 any が割り当てられます。
local foo: string = "bar"local x: number = 5
アノテーションで使用できる原始的な型は 4 つあります:
- nil - 値なし
- boolean - true または false
- number - 数値
- string - 文字列
Roblox 内では、すべてのクラス、データ型、列挙型には、それに対してチェックできる独自の型があります。
local somePart: Part = Instance.new("Part")local brickColor: BrickColor = somePart.BrickColorlocal material: Enum.Material = somePart.Material
型をオプションにするには、アノテーションの末尾に ? を使用します。
local foo: string? = nil
これにより、変数は指定された型(この場合は string)または nil のいずれかを許可します。
リテラル型
文字列やブール値を string や boolean の代わりにリテラル値にキャストすることもできます。
local alwaysHelloWorld: "Hello world!" = "Hello world!"alwaysHelloWorld = "Just hello!" -- 型エラー: 型 '"Just hello!"' を '"Hello world!"' に変換できませんlocal alwaysTrue: true = false -- 型エラー: 型 'false' を 'true' に変換できません
型キャスト
型チェックを支援するために、値を別の型に明示的にキャストする必要がある場合があります。これは :: 演算子を使用して行います。
local myNumber = 1local myString: stringmyString = myNumber -- 不可; 型変換エラーmyString = myNumber :: any -- OK; すべての式は 'any' にキャストできますlocal myFlag = myNumber :: boolean -- 不可; 型が関連していません
関数の型
次の関数を考えてみましょう。
local function add(x, y)
return x + y
end
この関数は x を y に加算しますが、どちらか一方または両方が文字列の場合はエラーになります。Luau はこの関数が数値のみを使用できることを知りません。このカテゴリの問題を防ぐために、パラメータに型を追加します。
local function add(x: number, y: number)
return x + y
end
Luau は、関数が 2 つの数値を受け取ることを知っており、数値以外のものを関数に渡そうとすると警告を出します。
add(5, 10)add(5, "foo") -- 型エラー: 文字列を数値に変換できません
返り値の型を定義するには、関数定義の末尾に : 演算子を置きます。
local function add(x: number, y: number): number
複数の型を返すには、型をかっこで囲みます。
local function FindSource(script: BaseScript, pattern: string): (string, number)
return 42, true -- 型エラー
end
関数型の定義
関数型は (in) -> out という構文を使用して定義できます。前述の関数の例を使用すると、関数の型は次のようになります。
type add = (x: number, y: number) -> numbertype FindSource = (script: BaseScript, pattern: string) -> (string, number)
テーブル型
Luau には table 型はありません。代わりに、テーブル型は {} 構文を使用して定義されます。テーブルを定義する一つの方法は {type} 構文を使用することで、リスト型を定義します。
local numbers: {number} = {1, 2, 3, 4, 5}local characterParts: {Instance} = LocalPlayer.Character:GetChildren()
インデックスタイプは {[indexType]: valueType} を使用して定義します。
local numberList: {[string]: number} = {Foo = 1,Baz = 10}numberList["bar"] = true -- 型エラー: ブール型を数値に変換できません
テーブルは、型内で明示的な文字列インデックスを持つこともできます。
type Car = {
Speed: number,
Drive: (Car) -> ()
}
local function drive(car)
-- 制限速度を守る
end
local taxi: Car = {Speed = 30, Drive = drive}
可変引数
任意の数の数値の合計を計算する関数は以下のようになります。
local function addLotsOfNumbers(...)
local sum = 0
for _, v in {...} do
sum += v
end
return sum
end
この関数は、任意の値を受け取り、無効な型(例えば string)を提供しても型チェッカーは警告を出しません。
print(addLotsOfNumbers(1, 2, 3, 4, 5)) -- 15print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- 数値に文字列を加算しようとしました
代わりに、... に型を割り当てます。これは他の型に割り当てるときの方法と同じです。
local function addLotsOfNumbers(...: number)
これにより、2 行目で型エラーが発生します。
print(addLotsOfNumbers(1, 2, 3, 4, 5))print(addLotsOfNumbers(1, 2, "car", 4, 5)) -- 型エラー: 文字列を数値に変換できません
ただし、これは関数型定義を書いているときには機能しません。
type addLotsOfNumbers = (...: number) -> number -- 期待される型、':'
代わりに、可変引数型を定義するには ...type 構文を使用します。
type addLotsOfNumbers = (...number) -> number
ユニオンとインターセクション
ユニオンまたはインターセクションを使用して、2 つ以上の型を持つ型を定義することもできます。
type numberOrString = number | stringtype type1 = {foo: string}type type2 = {bar: number}type type1and2 = type1 & type2 -- {foo: string} & {bar: number}local numString1: numberOrString = true -- 型エラーlocal numString2: type1and2 = {foo = "hello", bar = 1}
推論型の定義
推論型の型定義で typeof 関数を使用できます。
type Car = typeof({Speed = 0,Wheels = 4}) --> Car: {Speed: number, Wheels: number}
typeof を使用する一つの方法は、typeof 関数内で setmetatable を使用してメタテーブル型を定義することです。
type Vector = typeof(setmetatable({}::{x: number,y: number}, {}::{__add: (Vector, Vector|number) -> Vector}))-- Vector + Vector は Vector 型を返します
ジェネリクス
ジェネリクスは、基本的に型のパラメータです。以下の State オブジェクトを考えてみましょう。
local State = {Key = "TimesClicked",Value = 0}
ジェネリクスなしでは、このオブジェクトの型は次のようになります。
type State = {Key: string,Value: number}
しかし、Value の型を渡される値に基づいて決定したい場合、ここでジェネリクスが役立ちます。
type GenericType<T> = T
<T> は、任意のものに設定できる型を示します。これは置き換え型として視覚化するのが最良です。
type List<T> = {T}local Names: List<string> = {"Bob", "Dan", "Mary"} -- 型は {string} になりますlocal Fibonacci: List<number> = {1, 1, 2, 3, 5, 8, 13} -- 型は {number} になります
ジェネリクスには、かっこ内に複数の置き換えが含まれることもあります。
type Map<K, V> = {[K]: V}
前述の State オブジェクトを再構築してジェネリック型を使用するには:
type State<T> = {Key: string,Value: T}
関数のジェネリクス
関数もジェネリクスを使用できます。State の例では、関数の受け取る引数から T の値を推論します。
ジェネリック関数を定義するには、関数名に <> を追加します。
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>
型のエクスポート
型が ModuleScript の外で使用できるようにするには、export キーワードを使用します。
ReplicatedStorage におけるタイプモジュール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} said meow`)
end
}
newCat:Meow()