annotation
- is for expecting a specific type
- defines what a variable is allowed to be
- helps you find bugs
assertion
- is for overriding the linter (tells the script what a value definitely is)
- it helps you hide warnings
neither of them can ensure/change a type. they are purely visual in luau
just to be clear, you don’t need to strictly write strict code unless you want sexy code (normal code can be too).
strict code = more explict code. the benefit is predictability.
you should check the docs type checking @TaxFraudBruh already suggested this.
start with annotating your function parameters. it’s great for ModuleScripts
yes everything is a table but classes are treated as data types
i still want to emphasize that you should not cast types that aren’t guaranteed.
i’ll try to make some simple examples
unsafe casting/assertion:
saying Tool is always a Tool may give you an unexpected error during a play test. there won’t be a warning
--!strict
local Player = game.Players.LocalPlayer :: Player -- This is fine. LocalPlayer exists in LocalScripts
local Username = "BonesIsUseless"
local Money = 0
local MaxHealth = 20
local function FindTool(Username: string, ToolName: string): Tool?
local Backpack = Player:FindFirstChild("Backpack") :: Instance -- Backpack exists
-- NOT SAFE: What if "God Sword" doesn't exist?
-- This forces 'Tool' to be a Tool even if it is nil.
local Tool = Backpack:FindFirstChild(ToolName) :: Tool
print(Tool.Name)
return Tool
end
-- If i have no "God Sword", this will error when printing (nil).Name
local FavoriteWeapon = FindTool(Username, "God Sword")
safe casting:
here, only Player and Backpack gets asserted types
Tool’s class gets checked with a guard statement
--!strict
local Player = game.Players.LocalPlayer :: Player -- LocalPlayer exists in LocalScripts
local Username = "BonesIsUseless"
local Money = 0
local MaxHealth = 20
local function FindTool(Username: string, ToolName: string): Tool?
local Backpack = Player:FindFirstChild("Backpack") :: Instance-- Backpack exists
-- It would be better to check if the item exists, and if it's a tool
local Tool = Backpack:FindFirstChild(ToolName)
if not Tool or not Tool:IsA("Tool") then
warn(Username, "has no", ToolName, "tool")
return nil
end
print(Tool.Name)
return Tool
end
-- FavoriteWeapon's type should now be Tool?
local FavoriteWeapon = FindTool(Username, "God Sword")
redundant annotations:
--!strict
local Username: string = "BonesIsUseless"
local Money: number = 99999999999999
local MaxHealth: number = 20
helpful annotations:
annotating ambiguous Parameters/Variables
--!strict
local Player = game.Players.LocalPlayer :: Player -- LocalPlayer exists in LocalScripts
local Character = Player.Character or Player.CharacterAdded:Wait()
local StarterWeapons: {string} = {"God Sword"}
-- Make it clear that DealDamage requires a Model and Damage (number) value
local function DealDamage(Character: Model, Damage: number)
local Humanoid = Character:FindFirstChildOfClass("Humanoid")
if not Humanoid then return end
Humanoid.Health -= Damage
end
-- When using the 'DealDamage' function,
-- you can see that 'Character' should be a Model, and 'Damage' should be a number
DealDamage(Character, 100)
-- This would warn you that it expects a number, not a string
DealDamage(Character, "50") -- This wouldn't error
you do not need to read all of this. handling types is pretty straightforward