Checked paths
Walk from script to any object in your project and shot check knows what is there, so a misspelt child, a value of the wrong type or game is an error before anything runs.
A System project is a tree of folders, scripts and value objects (see One-file projects). shot check reads that tree and types every path in your .shot files from it: each child is a field of its parent, and each value object's Value has its class's type. A plain .luau file gets the same once it starts with --!strict.
A misspelt child
Get a name wrong and check points at it, before anything runs.
This page uses one small project, Shop.shotsystem: every command on it takes --project Shop.shotsystem. shot tree shows it:
Shop.shotsystem
./ (the project)
Settings/
Currency (StringValue)
Price (NumberValue)
Shop/ (folder module)
Shop.shot
Types.shotCurrency holds "Coins", Price holds 25 and has an attribute Max = 100, and Shop/Types.shot is empty. Shop reaches the settings by a path from script:
Shop/Shop.shot
types script.Parent.Types
local settings = script.Parent.Parent.Settings
module Shop = {
function PriceText() : string
return settings.Price.Value .. " " .. settings.Curency.Value
end
}shot check says:
Shop.shotsystem/Shop/Shop.shot:7:41 TypeError: Key 'Curency' not found in external type 'Folder'
1 errors in 1 file (2 files checked, 0.1 s)The column is where the path starts. Write settings.Currency and it says:
0 errors (2 files checked, 0.1 s)Where a path starts
Every path starts at script and climbs one folder per .Parent.
In Shop/Shop.shot:
script | Shop.shot itself, a ModuleScript (.shot and .luau files run as modules) |
script.Parent | the Shop folder, a ModuleScript too because it holds Shop.shot |
script.Parent.Parent | the project's root, a Folder named after the project |
script.Parent.Parent.Parent | an Instance? to check, and nil when the code runs: nothing is above the root |
A plain folder, like Settings, is a Folder. root is the project's root only in scenarios (see Running a project): in a .shot or .luau file it is Unknown global 'root', so climb from script.
Values have Types
A value object's Value has its class's type, so a string can't slip into a sum or a comparison.
Add a function to Shop that compares coins with the wrong value:
function CanAfford(coins : number) : boolean
return coins >= settings.Currency.Value
endand check says:
Shop.shotsystem/Shop/Shop.shot:11:10 TypeError: Expected this to be 'number', but got 'string'
1 errors in 1 file (2 files checked, 0.1 s)Compare with settings.Price.Value and it is 0 errors again. The type of Value, by class:
StringValue | string |
NumberValue, IntValue | number |
BoolValue | boolean |
Color3Value | Color3 |
Vector3Value | Vector3 |
ObjectValue | Instance? (it may point at nothing) |
No game, no services
Code brought over from a Roblox game is caught where it reaches for game.
A project's root has nothing above it: no DataModel, so no game, no workspace and no Roblox services. Start the path from game instead of script:
local settings = game.ReplicatedStorage.Settingsand check says:
Shop.shotsystem/Shop/Shop.shot:3:18 TypeError: Unknown global 'game'; consider assigning to it first
1 errors in 1 file (2 files checked, 0.1 s)The fix is the path from script, script.Parent.Parent.Settings. workspace, Instance (so Instance.new), Enum and task are unknown globals the same way.
Rename an object
Rename an object and check finds every line that still uses the old name.
shot set Settings/Currency Name Money renames Currency:
renamed Settings/Currency.instance to Settings/Money.instance
files changed: Settings/Currency.instance, Settings/Money.instance (shot check shows what this breaks, if anything)The object commands (create, destroy, set, attr, tag) don't run the check themselves. shot check then says:
Shop.shotsystem/Shop/Shop.shot:7:41 TypeError: Key 'Currency' not found in external type 'Folder'
1 errors in 1 file (2 files checked, 0.1 s)Fix those lines, or take the rename back with shot undo (and check is back to 0 errors).
Attributes
Read an attribute and check makes you look at what you got.
An attribute is not a child, so settings.Price.Max is an error:
Shop.shotsystem/Shop/Shop.shot:15:10 TypeError: Key 'Max' not found in external type 'NumberValue'Read it with GetAttribute("Max"). That is unknown?, since the attribute may be missing or hold anything, so returning it from a function that returns a number is an error too:
Shop.shotsystem/Shop/Shop.shot:15:3 TypeError: Expected this to be 'number', but got 'unknown?' caused by: Not all union options are compatible. Expected this to be 'number', but got 'unknown'Check its type first:
function Limit() : number
local max = settings.Price:GetAttribute("Max")
return if type(max) == "number" then max else 0
endA property hides a child
Name a child like a property and the property wins, in check and when the code runs.
Add a StringValue called Name to Settings (shot create StringValue Settings Name) and read it:
function Title() : string
return settings.Name.Value
endsettings.Name is the folder's own name, a string, so check says:
Shop.shotsystem/Shop/Shop.shot:20:10 TypeError: Key 'Value' not found in table 'typeof(string)'Rename the child (shot set Settings/Name Name Title) and read settings.Title.Value: 0 errors. The same goes for any name an instance already has, such as ClassName or Clone.
Finding a child by name
Look a child up by name and you keep its type, as long as the name is one the tree has.
FindFirstChild("Currency") | a StringValue, like settings.Currency |
FindFirstChild("Curency") | an Instance?, so reading .Value on it is an error |
FindFirstChild(name) | an Instance? too, for a name held in a variable |
GetChildren() | a list of Instance, {Instance} |
WaitForChild("Currency") | an error in a .shot file: instances here have no WaitForChild |
Instances here have no events either: settings.Price.Changed is a Key 'Changed' not found error.
Plain Luau files
A plain .luau module gets the same checked paths when its first line is --!strict.
Shop/Format.luau
--!strict
local settings = script.Parent.Parent.Settings
local Format = {}
function Format.Price(amount : number) : string
return amount .. " " .. settings.Curency.Value
end
return Formatshot check says:
Shop.shotsystem/Shop/Format.luau:7:26 TypeError: Key 'Curency' not found in external type 'Folder'
1 errors in 1 file (3 files checked, 0.1 s)Without the --!strict line the same file checks with 0 errors: Luau's looser mode lets a path kept in a local like settings through. A WaitForChild in a .luau file isn't reported either; it fails when the line runs.
What it doesn't check
- Scenarios:
shot checkdoesn't look at them (see Running a project). A misspelt child in one fails when it runs, withCurency is not a valid member of Settings. shot check --fast: it skips the type check, so it finds no path mistakes.- An empty folder: a folder with nothing in it isn't in the tree check reads, so a path to it is
Key 'Sales' not found in external type 'Folder'until something is put in it. - A cast:
x :: StringValueis taken at its word.
Errors
TypeError: Key 'Curency' not found in external type 'Folder'
TypeError: Expected this to be 'number', but got 'string'
TypeError: Unknown global 'game'; consider assigning to it first
TypeError: Unknown global 'root'; consider assigning to it first
TypeError: Key 'Max' not found in external type 'NumberValue'
TypeError: Expected this to be 'number', but got 'unknown?' caused by: Not all union options are compatible. Expected this to be 'number', but got 'unknown'
TypeError: Key 'Value' not found in table 'typeof(string)'
TypeError: Value of type 'Instance?' could be nil
TypeError: Key 'Value' not found in external type 'Instance'
TypeError: Key 'Changed' not found in external type 'NumberValue'
WaitForChild is Roblox's: a ShotSystem instance has no WaitForChild (everything in the project is there when its code runs), so write the child's name, or FindFirstChild when it may be missing