Primitives and Simple Types
Builtin types
Section titled “Builtin types”The Luau VM supports 10 primitive types:
nilstringnumberbooleantablefunctionthreaduserdatavectorbuffer
Most of these can be specified by their name and written directly in type annotations:
Some types have special syntax:
tableandfunctionare not represented by name, but have their dedicated syntax as covered in this syntax documentuserdatais represented by concrete types, andvectoris not representable by name at all
The type checker also provides the builtin types unknown, never, and any.
Special behavior with nil
Section titled “Special behavior with nil”There’s a special case where we intentionally avoid inferring nil for local variables. This allows you to declare variables first and assign values to them later — if we inferred nil, you wouldn’t be able to assign other types to these variables.
unknown type
Section titled “unknown type”unknown is also said to be the top type, that is it’s a union of all types.
Unlike any, unknown will not allow itself to be used as a different type!
In order to turn a variable of type unknown into a different type, you must apply type refinements on that variable.
never type
Section titled “never type”never is also said to be the bottom type, meaning there doesn’t exist a value that inhabits the type never. In fact, it is the dual of unknown. never is useful in many scenarios, and one such use case is when type refinements proves it impossible:
any type
Section titled “any type”any is just like unknown, except that it allows itself to be used as an arbitrary type without further checks or annotations. Essentially, it’s an opt-out from the type system entirely.
Function types
Section titled “Function types”Let’s start with something simple.
In strict mode, the inferred type of this function f is <A>(A) -> A (take a look at generics), whereas in nonstrict we infer (any) -> any. We know this is true because f can take anything and then return that. If we used x with another concrete type, then we would end up inferring that.
Similarly, we can infer the types of the parameters with ease. By passing a parameter into anything that also has a type, we are saying “this and that has the same type.”
Variadic types
Section titled “Variadic types”Luau permits assigning a type to the ... variadic symbol like any other parameter:
f accepts any number of number values.
In type annotations, this is written as ...T:
Type packs
Section titled “Type packs”Multiple function return values as well as the function variadic parameter use a type pack to represent a list of types.
When a type alias is defined, generic type pack parameters can be used after the type parameters:
Keep in mind that
...Tis a variadic type pack (many elements of the same typeT), whileU...is a generic type pack that can contain zero or more types and they don’t have to be the same.
It is also possible for a generic function to reference a generic type pack from the generics list:
Generic types with type packs can be instantiated by providing a type pack:
There are also other ways to instantiate types with generic type pack parameters:
Trailing type pack argument can also be provided without parentheses by specifying variadic type arguments:
Type pack parameters are not limited to a single one, as many as required can be specified:
Singleton types (aka literal types)
Section titled “Singleton types (aka literal types)”Luau’s type system also supports singleton types, which means it’s a type that represents one single value at runtime. At this time, both string and booleans are representable in types.
We do not currently support numbers as types. For now, this is intentional.
This happens all the time, especially through type refinements and is also incredibly useful when you want to enforce program invariants in the type system! See tagged unions for more information.