Type checking allows us to verify the data types of variables, properties, and values in our Roblox scripts. This makes our code easier to understand for both us and other developers, as well as enabling Studio to provide us better autocomplete suggestions.
What types are available?
Before we dive more into why we should use type checking or even how to use it, we should ensure that you understand the basics by going over what types are available to us.
Primitive Types
The types provided to us by Roblox’s Luau are referred to as “primitive types” and those are the following:
Type | Example |
---|---|
string | “Example” |
number | 1 |
boolean | true |
table | {} |
nil |
Roblox Types
Roblox adds to their plethora of custom types often, but you always view the most up to date list here! Here are a few of the commonly used types:
- Player
- Part
- Model
- Folder
How to enable Type Checking
While learning, it’s a good idea to use the strict type inferring mode, so we can spot possible issues!
You can enable strict mode by putting the following comment at the top of your script: -!strict
.
--!strict
local test = 1
Type Inferencing
When we create a variable and assign a value to it, Studio is able to automatically know what type the variable is. In many occasions where we don’t explicitly tell Studio what type a value is, Studio may be able to infer what it most likely is.
local test = 1
With the example able, Studio is able to infer that test is a number.
Type Annotation
To specify the type of something like a variable or a function’s parameter we’ll use something called a type annotation. Here is an example of how we can do this:
local test: number = 10 -- Pointless since it's already inferred
local function testing(player: Player, amount: number)
end
When creating a variable or a function’s parameter, simply put a semicolon (:
) after the name and then put the type.
Optional Parameters
What if we create a function and specify the parameter types, but maybe we don’t want to always pass through the second parameter when we call this function? We can use a question mark (?
) after specifying the parameter’s type to indicate that parameter isn’t required!
local function RenamePlayer(player: Player, name: string?)
name = name or "Default"
player.Name = name
end
RenamePlayer(player) -- Works
RenamePlayer(player, "test") -- Works
Function Return Type
If we have a function which returns something, we can specify this by annotating it after listing the parameters!
local function GetSum(num1: number, num2: number): number
return num1 + num2
end
Union Types
What if we have a variable, which could be a different type at multiple points? We can use a bar (|
) to basically act as the word or when annotating types!
local answer: boolean | string | number = "Yes"
answer = true
answer = 5
Singleton Types
You can create types which are very specific!
local test: "Yes" | "No" = "Yes"
local test2: 1 | 2 | 3 = 2
Creating Custom Types
Creating a type is similar to creating a variable!
type test = number
type test2 = number | string
Using types from another Script
Sharing types amongst scripts is a common thing. Personally, I like to make one Module Script in the Replicated Storage where I store all my custom types!
You can use the keyword export
to allow other scripts requiring the module to use those custom types.
local Types = {}
export type Pet = {
Name: string,
Rarity: string
}
export type PlayerData = {
Cash: number,
Gems: number,
Pets: {
[string]: Pet
}
}
return Types
local Types = require(ReplicatedStorage.Types)
local Template: Types.PlayerData = {
Cash = 0,
Gems = 0,
Pets = {}
}
Type Refinement
A common scenario I find myself in, is I’ll loop through some children within either a Gui or even a Folder in my Workspace. The children I’m looking for might be Parts, but within that same Folder could be Models or other objects. When doing this, issues can arise with auto-completion, because Studio doesn’t infer what we looped through are parts.
for _, possiblePart in Workspace.Parts:GetChildren() do
if not possiblePart:IsA("Part") then continue end
local part: Part = possiblePart
part.BrickColor = BrickColor.new(1)
end
Assigning a type to a Module Variable
If you create a variable apart of a Module, you’ll realize that you cannot actually annotate its type. There is a workaround to this, which to be honest, I’m not a huge fan of and usually don’t use, but it is nice to know!
What we can do is create a local variable and annotate its type, then assign the Module variable to be the value of that local variable.
local module = {}
local numbers: number = 0
module.Numbers = numbers
return module
Other Resources
With this article, we went through most of the common and basic scenarios to help you understand type checking. Here are a few additional resources you can learn from: