Luau Type Checking Beta!

Please DO NOT publish places with type annotations just yet as they will not work on production! This is why it’s a beta :slight_smile:

However, please continue to experiment in Studio and give us feedback. We are reading everything and will be fixing reported bugs and discussing syntax / semantics issues some people brought up.


We’ve been quietly working on building a type checker for Lua for quite some time now. It is now far enough along that we’d really like to hear what you think about it.

I am very happy to offer a beta test into the second half of the Luau effort.

Beta Test

First, a word of caution: In this test, we are changing the syntax of Lua. We are pretty sure that we’ve mostly gotten things right, but part of the reason we’re calling this a beta is that, if we learn that we’ve made a mistake, we’re going to go back and fix it even if it breaks compatibility.

Please try it out and tell us what you think, but be aware that this is not necessarily our final form. :slight_smile:

Beta testers can try it out by enabling the “Enable New Lua Script Analysis” beta feature in Roblox Studio.


Luau is an ahead-of-time typechecking system that sits atop ordinary Lua code. It does not (yet) feed into the runtime system; it behaves like a super powerful lint tool to help you find bugs in your code quickly.

It is also what we call a gradual type system. This means that you can choose to add type annotations in some parts of your code but not others.

Two Modes

Luau runs in one of two modes: strict, and nonstrict.

Nonstrict Mode

Nonstrict mode is intended to be as helpful as possible for programs that are written without type annotations. We want to report whatever we can without reporting an error in reasonable Lua code.

  • If a local variable does not have a type annotation and it is not initially assigned a table, its type is any
  • Unannotated function parameters have type any
  • We do not check the number of values returned by a function
  • Passing too few or too many arguments to a function is ok

Strict Mode

Strict mode is expected to be more useful for more complex programs, but as a side effect, programs may need a bit of adjustment to pass without any errors.

  • The types of local variables, function parameters, and return types are deduced from how they are used
  • Errors are produced if a function returns an inconsistent number of parameters, or if it is passed the wrong number of arguments

Strict mode is not enabled by default. To turn it on, you need to add a special comment to the top of your source file.


New syntax

You can write type annotations in 5 places:

  • After a local variable
  • After a function parameter
  • After a function declaration (to declare the function’s return type)
  • In a type alias, and
  • After an expression using the new as keyword.
local foo: number = 55

function is_empty(param: string) => boolean
    return 0 == param:len()

type Point = {x: number, y: number}

local baz = quux as number

Type syntax

Primitive types

nil, number, string, and boolean


The special type any signifies that Luau shouldn’t try to track the type at all. You can do anything with an any.


Table types are surrounded by curly braces. Within the braces, you write a list of name: type pairs:

type Point = {x: number, y: number}

Table types can also have indexers. This is how you describe a table that is used like a hash table or an array.

type StringArray = {[number]: string}

type StringNumberMap = {[string]: number}


Function types use a => to separate the argument types from the return types.

type Callback = (string) => number

If a function returns more than one value, put parens around them all.

type MyFunction = (string) => (boolean, number)


You can use a | symbol to indicate an “or” combination between two types. Use this when a value can have different types as the program runs.

function ordinals(limit)
    local i = 0
    return function() => number | nil
        if i < limit then
            local t = i
            i = i + 1
            return t
            return nil


It’s pretty commonplace to have optional data, so there is extra syntax for describing a union between a type and nil. Just put a ? on the end. Function arguments that can be nil are understood to be optional.

function foo(x: number, y: string?) end

foo(5, 'five') -- ok
foo(5) -- ok
foo(5, 4) -- not ok

Type Inference

If you don’t write a type annotation, Luau will try to figure out what it is.

local Counter = {count=0}

function Counter:incr()
    self.count = 1
    return self.count

print(Counter:incr()) -- ok
print(Counter.incr()) -- Error!
print(Counter.amount) -- Error!

Future Plans

This is just the first step!

We’re excited about a whole bunch of stuff:

  • Nonstrict mode is way more permissive than we’d like
  • Generics!
  • Editor integration

This topic was automatically opened after 11 minutes.

Thank You!

I could not believe my eyes @zeuxcg said type checking was coming as a beta feature this week.
The fact that it actually came is amazing! I immediately thought of a use case.

I often work with creating my own “classes” and I find it a hassle to have to check if the passed datatypes are the correct type.

I assume type checking fixes this problem!


I am a little questionable about whether the syntax will ever be cleaner.

In my opinion arrow function like syntax do not really fit into the style of Lua, and that is something I expect to see in a curly bracket based language like Javascript.


Thank you so much for this! I’ve been waiting for an excuse to unsloppify my assert statements for so long <333

Just look at this mess I can finally say goodbye to from the start of all of my functions:

assert(typeof(arg1) == "number", "numbers only ples")
assert(typeof(arg2) == "number", "nooo numbers plssss")
assert(typeof(arg3) == "number", "stop it hurts ;-;")

Now the start of my functions can look like this:

-- look mum no asserts

But seriously, this is a revolutionary step forward, especially for large projects like many I’ve worked on before. I used to make excuses to not write those ugly lines of code. Now that it’s not only quick to write, but easier to read and takes up less lines on the screen, I have no excuses. Thank you for making the lazy codebases of all developers everywhere at least a bit less egregious :sweat_smile:


Love this update! Really makes it feel like a more strict language. Hope we can see this expand to intellisense soon, which’ll make this update amazing.

All of my spawn() calls almost immediately became angry as soon as I turned this on. Not sure why?


I love this! This should significantly improve QoL for developers like me, who don’t want assert statements littering the top of each function. Thanks!


Will this cause certain games, particularly older ones, to stop working?


Are there plans for intellisense to be upgraded to show what type is expected when calling a function, given that custom functions do show up in intellisense (e.g. after requiring a module)? As well, are there any plans for us to be able to specify annotations for intellisense so we can add custom method details?


The feature has to be enabled for it to work and for --!strict to appear at the top of the script. So I would assume no.


I think they mean that they’re adding to it, as none of the changes we see here break anything so far, but definitely add a lot.

(adding since I don’t wanna bloat the thread)
I’m pretty sure that’s what “Editor integration” means in the future plans.


This is awesome!

When can we expect to get API access so 3rd party tools can have type checking and linking?

Will this type checker have syntax definitions available?

What level of support can we expect?

I remember 3rd party tooling being one of the key requirements for this system when mentioned. Is this still a requirement?


From the main post, I think you might be in luck wrt integrating tools:

A bit vague, but I believe this means we’re going to get programmatic access to the type system at some point :slightly_smiling_face:

edit: realised you might also be talking about stuff like language servers to help with integrating it into places like vscode, in which case maybe ignore the above lol


Yeah. I’m confident Roblox Studio will have support. I’m more concerned about other editors.

Also, Roblox is a large enough company to push a modification of a language. It would be neat to see applications of this outside of Roblox.

local x: number = 10
x = "hahaha" as any



This new type checking beta sounds great and interesting with the clean looking syntax.

You know people are already excited for this when you see syntax changes from or to | and table types being a thing in this beta. I’ve also heard yesterday from fellow friends that “the mess of assert() statements piling functions should be replaced with something neater”, and this is what has came up today.

So excited to see this coming out of beta very soon! The people at Roblox have pushed a lot of absolutely great updates for developers since I’ve hopped onto the forums for the first time, and you definitely can’t call this an exception.


just don’t do that, and if someone does that, oof them with a linked sword. I’m sure we all have coding conventions (and maybe even linters?)

Keep in mind, this is a step up from completely untyped, vanilla Lua.


Hey, it works in typescript too, iirc. At least we don’t have 2 ways to cast things (yet)!


Yep, but we banned the use of any in roblox-ts. There’s defined and unknown. instead.


I am personally not happy about the syntax changes, but I am happy about the introduction of type checking.

I see Lua as a language built to be picked up easily, not one to be confusing. It is a lot easier for someone to understand what a keyword or means rather than what a keyword | means.

The update is great but I feel the syntax needs significant changes.