Hello community
I made this topic to help those who want to enter the world of typechecking, but do not know where to start or how, and also for those who have doubts about some topics. I hope this topic helps you! If so, I would like you to like this topic so that it can reach more people and help more developers!
Why read this Topic?
The reasons why you should read this topic are:
- Explanations with
code examples
- Reasons
why certain concepts are useful
will be given. - Sources are given with which you can continue to learn
Typechecking
on your own - You can contribute to the Topic by correcting explanations or information to always have updated and real information!
Some topics may be explained wrongly or incorrectly, I would appreciate a correction in the comments to avoid misinforming the community.
Topics to be discussed in this Topic
- What is typechecking?
- Type inference in LUAU
- Type annotation
- built-in types (primitive types)
- string
- number
- boolean
- table
- function
- Optional types (
?
operator) - unknown, any and never
- How to type a table and dictionaries
- Index signatures (index types)
- Type aliases (
type
keyword)
These topics will be added in the future (i’m tired)
Function types (functional types)Union typesUnion types with built-in typesIntersection typesSingleton types (literal types)Type casting (::
operator)Type inference withtypeof
methodType inference modes (nocheck, nonstrict, strict)MORE COMING SOON
Click on the dropdowns to see the content of each section
1. What is typechecking
1. What is typechecking?
Let’s imagine you’re baking a cake. Each ingredient you use, like flour
, sugar
, or eggs
, has a specific role in the recipe. Now, if you mistakenly use salt instead of sugar
, the cake won’t turn out right.
In programming, each piece of data (like numbers, text, or more complex data structures) is like an ingredient. They have a “type” (like number, string, etc.) that determines what you can do with them.
Type checking is like the kitchen assistant who checks your ingredients before you add them to the mix. If you’re about to add salt where the recipe calls for sugar, type checking raises an alarm, saying “Hey, you can’t use that here!”
In summary, type checking is a crucial aspect of programming that contributes to error detection, code reliability, optimization, documentation, and abstraction. It’s like a safety net, helping to catch and prevent potential issues in your code.
2. Type inference in LUAU
2. Type inference in LUAU
First of all: what is type inference?
Type inference is LUAU’s way of “GUESSING” what type of data a variable or an expression is. This is useful for example for autocompletion, but there are times when LUAU does not know what type of data something is, so we will have to help LUAU to know what type of data something is.
Example:
LUAU can not only infer the data type of a literal value, but it can also infer the data type of an expression!
Now knowing what type inference is, we can move to the next section
3. Type annotation
3. Type annotation
Type annotation is a way of STRICTLY indicating the data type of a variable.
The Type Annotation will be used to type a variable with a specific data type, for security or auto-completion purposes.
How to use Type Annotation?
To use the Type Annotation, it will be done with the :
operator. In this way:
local variable: data type = value of that data type
Actual use of Type Annotation for autocompletion
local player = game.Players.LocalPlayer
local character = player.Character
local humanoid: Humanoid = character:WaitForChild("Humanoid")
humanoid.WalkSpeed = 12
And what happens if you pass another type of data to the one specified in the variable?
What will happen is that LUAU will give us a warning, telling us that we cannot pass a data type other than the one we specified. Example:
IT IS IMPORTANT TO KNOW THAT ROBLOX WILL NOT GIVE AN ERROR IF WE EXECUTE THE CODE, THIS IS ONLY USEFUL TO AVOID BEHAVIORS IN THE CODE THAT WE DO NOT WANT.
We can also use data types that Roblox has already made for us! For example the data type Model
.
local someModel: Model = workspace.AmongUs
--[[
This is not so useful since roblox will not know what objects are
inside that model or if objects even exist inside it, so it is not the best
use of this type of data.
]]
4. built-in types (primitive types)
4. built-in types (primitive types)
Primitive types are data types that come with the LUA language, and are the simplest and most commonly used data types.
These data types can be used to write variables, function parameters or table properties.
Example of how to type variables with these primitive data types:
--[[
Most common primitive data types:
string, number, table, function, boolean
]]
local thisIsAString: string = "hi"
local aRandomNumber: number = 53952
local aBoolean: boolean = true -- or false
local anEmptyTable: {} = {}
local aFunction: () -> () = function() end
5. Optional types (? operator)
5. Optional types (? operator)
The ?
operator is used to indicate that a variable can be of the specified data type or nil. And in the case that we put another type of data different from the one we specify, LUAU will give error in the same way, but if we pass nil
, it will not give any warning.
This operator can be used to indicate that a function can receive a parameter of a data type, or it can not receive it. For example:
local function foo(a: number, b: number?) -- b is optional
b = b or 10 -- assigning a default value in the event that a value is not passed
print(a, b)
end
foo(1, 4) -- correct, no warnings
foo(1) -- correct, no warnings (If the optional type operator were not used, this would result in an error)
To use this operator, you must add
?
at the end of the specified data type
6. unknown, any and never
6. unknown, any and never
any
The any type in Luau is similar to unknown, but it’s used when the type inference falls back to a default type. It’s not a type that you would use in type annotations. Instead, it’s a type that Luau uses internally during type inference.
Also note that “any” can represent ANY type of data, be it a number, string, boolean, table, ect. And because of this, luau will let us perform any kind of operation with this data type without having to check the data type of the parameter.
-- it is not necessary to set `:any`, if not set, luau will infer the parameters as `any` anyway.
local function addNumbers(a, b)
return a + b -- No warnings
end
Cases in which any
may appear
- When the data type of a parameter is not specified
- When the return type of a function (e.g. WaitForChild) is unknown.
- When it is used strictly (when we put it in some variable or parameter)
Unknown
Unknown is a data type similar to ANY, the main difference is that this data type will not let us perform operations or use methods until we know what its data type is. This datatype is often used in remote events
as a small layer of security.
To find out what the data type of a unknown
parameter (or variable) is, we can use the typeof
or type
function.
Basic example:
-- without checking the data type
local function addNumbers(a: unknown, b: unknown)
return a + b -- warning: type unknown could not be converted into number
end
-- checking the data type
local function addNumbers(a: unknown, b: unknown)
if type(a) == "number" and type(b) == "number" then
return a + b
end
end
Because LUAU already knows that “a” and “b” are numbers, then it will let us perform the operations that numbers allow. This can also work for any type of data.
never
The never type in Luau represents a value that never occurs. It’s used in situations where a function never returns a value. For example, a function that always throws an error could have a return type of never.
function throwError(): never
error("This function always throws an error")
end
In this example, the throwError
function never returns a value because it always throws an error, so its return type is never
.
Summary:
- any: The data type that represents ALL possible data types (we can perform operations with this data type without receiving warnings).
- unknown: A data type similar to “any” but more useful and powerful. This datatype will not let us perform operations on the value until we know what type of datatype it is.
- never: A data type used to indicate that something will never be executed or stops the execution of the current code.
7. How to type a table and dictionaries
7. How to type a table and dictionaries
Knowing how to type tables and dictionaries is important to establish mandatory properties with a specific data type for each property. Also knowing how to type tables and dictionaries will help us to have better autocompletion in these.
If a table is not correctly typed, it will be inferred by LUAU by giving the data type “any” to each property or element. And let’s remember to always avoid typing our stuff with “any” for code safety.
The tables or dictionaries will be inferred correctly if we use the strict mode, but we will not see this in this Topic until a future time
Syntax for typing tables and dictionaries
Tables
In order to type tables, we can use the literal syntax (using {}
) to do so. Inside the {}
we can put any datatype we want the table to contain. For example:
local myTable: { dataType } = { only values of the specified data type }
-- Example:
local myTable: { string } = { "hi", "bye", "like" }
In the previous example: {string}
means that we want that table to contain only any number of strings as content. If we pass another type of data, LUAU will give us a warning.
local myTable: { string } = { "hi", "bye", "like" } -- Correct!
local myTable2: { string } = { "hi", "bye", "like", 20, true, false } -- Nuh uh, incorrect
Not only can we use string
, we can use any data type, such as boolean
.
local onlyBoolean: { boolean } = { true, false, false, true, true, ... }
Type nested tables
To type nested tables, we can do it using tables within tables as type annotation:
local nestedTableStrings: { { string } } = { { "hi" }, { "bye" } } -- Correct
local nestedTableStrings2: { { string } } = { "hi", "bye" } -- Incorrect
Typing tables in this way is not a good idea as it does not allow you to create complex data types. This can be fixed with index-types and type aliases (see next sections).
Dictionaries
In Lua, a dictionary is a data structure that holds key-value pairs. Each key in the dictionary is unique and is used to access its corresponding value. Here’s how you can define a dictionary:
local dict: { keyName: string } = { keyName = "hello" }
Nested Dictionaries
Dictionaries can also contain other dictionaries as values. This is known as nested dictionaries. Here’s how you can define a nested dictionary:
local dict: {
innerDict: {
property: string,
},
normalProperty: string,
} = {
innerDict = { property = "hello" },
normalProperty = "hello"
}
Type Checking
Luau provides strict type checking to ensure that the values in the dictionary match the expected types. If a value of a different type is assigned to a key, Luau will throw an error.
--!strict
local dict: { keyName: string } = { keyName = 20 } -- Error
However, if strict mode is not enabled, Luau will not throw an error even if the types don’t match.
local dict: { keyName: string } = { keyName = 20 } -- No errors
8. Index signatures (index types)
8. Index signatures (index types)
Index types in LUAU, also known as index signatures, allow you to create dictionaries where the keys are not known beforehand but the type of the values is known. This is particularly useful when you want to create a dictionary that can hold an arbitrary number of keys, all of which have values of the same type.
Here’s how you can define a dictionary using index types:
local dict: { [index: string]: string } = { key1 = "hello", key2 = "world" }
In this example, index can be any string, and the value corresponding to index will be of type string. This means you can add as many keys as you want to dict as long as the values are strings.
Index types are powerful because they provide flexibility. You don’t need to know all the keys at the time of dictionary creation. You can add keys dynamically at runtime, which is not possible with regular dictionaries.
However, with great power comes great responsibility. When using index types, you need to ensure that the values you assign to the keys match the expected type. If not, Luau will throw a type error.
Here’s an example:
--!strict
local dict: { [index: string]: string } = { key1 = "hello", key2 = 20 } -- Error
In this example, we’re trying to assign a number to key2 which is expected to be a string. This results in a type error.
9. Type aliases (type keyword)
9. Type aliases (type keyword)
Type aliases are a way to create your own data types and use them to type things.
They allow us to have more readability in our code and allow us to have more complex data types.
To create a type alias you must do it with the following syntax:
type DataTypeName = value
-- Example of a type alias
type Person = {
name: string,
age: number,
hobbies: { string }
}
How can they be used to type?
We will simply use the same syntax as the type annotation. In this way:
type SomeType = { hi: string }
local variable: SomeType = { hi = "Hi!" }
Export Type Aliases
Something interesting and useful that we can do, is to create a separate ModuleScript in which we have all our type aliases created to be able to use them in any script.
In order to export a data type, we need to do it with the keyword export
followed by the type alias. Example: export type Person = {}
.
To be able to use the data types that we have exported, it is done in this way:
-- Types.lua (ModuleScript)
export type Person = {
age: number,
name: string,
}
return nil -- We do not need to return any specific value if we are only defining type aliases.
-- SomeScript.lua
local Types = require(some_path.Types)
-- Now if we want to use some specific type of data, we must do it in the following way:
local me: Types.Person = { age = math.huge, name = "Isaac" }
We can use any data type within the type aliases
Topic updates
18/02/2024 - 1.0.0
- Topic published
Resources
luau-lang: Type checking - Luau
Roblox Studios Documentation: Type Checking | Documentation - Roblox Creator Hub
A 2021 post by @intelsyntax_enjoyer: Type checking for beginners!
Video by Suphi Kaner: https://youtu.be/eRVDL7xlN8g?si=sblphfns9ybNP0tE
Video by MonzterDEV: https://youtu.be/LVx4RhXp2Jw?si=d0XWyJdyLpI47FbY
I will be updating this Topic little by little, so if you don’t understand something, you can ask me in the comments to try to explain it.
Contribution
Help correct misspelled things, content ideas or report poorly explained things to appear here!
Spelling Corrections: @King_GamingRobloxYT
Topic Corrections: @GameInspectors (category corection)