Luau Type Checking Release

Attempting to iterate over nil or a non-any type variable instantly crashes studio before execution.

examples

--!strict

for _ in pairs() do
end
--!strict

local a = function(x: string)
	for _ in pairs(x) do
	end
end

Please report all issues like this as bugs - we have to currently manually specify type information for certain types like Color3, so things slip through. The two issues above will be fixed!

3 Likes

Should be fixed after Studio restart, this one change wasn’t fully ready and was missed in a pre-release flurry of flags :slight_smile:

1 Like

Awesome! I added a third to that post as well just as you responded (Region3.new() can take in 0 arguments in its constructor, but nonstrict mode reports it needs 2 arguments)

image

Will Luau ever become open-source? Now that this is fully released I think it’s a possibility. Who wouldn’t want to use Lua variable types in a HttpService backend for their Roblox game?

3 Likes

have you tried reinstalling ur studio? if not try following this steps to completely clear registries and stuff in case u have corrupt install

1 Like

Since this is basically static coding, is it going to improve performance? If so, by how much and is it going to change how scripts run to be more like “compiled” scripts rather than “interpreted” scripts?

1 Like

Could you create a separate report for these, preferably with source code pasted so that we can reproduce directly?

2 Likes

Wow, this is just going to be really helpful when scripting

Is it a bug that this code warns?

--!strict
local i:Part? = Instance.new"Part"
if typeof(i) == "Instance" then
	print(i.Anchored) -- W000: Key 'Anchored' not found in class 'Instance'
end

If i is an instance, it has to be a Part because a Part satisfies the condition but nil doesn’t.

This also looks like a bug

--!strict
local i:Part? = Instance.new"Part"
if i then
	i:Destroy()
	i = nil -- W000: Type 'Part' could not be converted to 'nil'
end

So does this mean that something like

--!strict
local str1:string = "abcde"
-- W000: Function only returns 1 values. 5 are required here
-- W000: Type 'nil|number' could not be converted into 'number'
local a:number,b:number,c:number,d:number,e:number = string.byte(str1,1,-1)
-- W000: Argument count mismatch. Function expects 1 argument, but 5 are specified
local str2:string = string.char(a,b,c,d,e)

Will no longer warn for mismatches of return values and arguments?


Errors for mixing union and intersection appear on the wrong line
image
Is there a reason this isn’t allowed? Presumably there could be precedence like and/or operators.

With function types what is the precedence of them with unions/intersections?

type a = (b) -> c&d

Could either be parsed as

type a = ((b) -> (c))&d

or

type a = (b) -> (c&d)

And this case happens even if you specify parenthesis around the return value

type a = (   b   )   ->   (   c   )   &   (   d   )
--       |       |        |-------|       |-------|
--       |       |        | type  |       | type  |
--       |-------|        |-----------------------|
--       | type  |        | type                  |
--       |----------------------------------------|
--       | type                                   |

type a = (   b   )   ->   (   c   )   &   (   d   )
--       |-------|        |-------|       |       |
--       | type  |        | type  |       |       |
--       |------------------------|       |-------|
--       | type                   |       | type  |
--       |----------------------------------------|
--       | type                                   |

Will there be documentation for types? It would help quite a lot for weird cases like precedence of function types and unions/intersections.

1 Like

Sure, here’s a model file for each one:

Key 'constructor' not found in table '{ @metatable any }'

IndexedEvent.rbxm (4.1 KB)
Should be in the module “IndexedEvent”, line 90
image

Key 'constructor' not found in table 'Spring'

Spring.rbxm (2.9 KB)
Line 142
image

1 Like

I think they have a tab on performance here:
link
Not sure if you’ve checked, or it doesn’t answer the question but I tried anyways

The whole “Anchored” not showing up in Instance is caused by something simple. Instance does not have BasePart properties, make sure it’s actually a type BasePart. If you use a question mark that means it doesn’t have to be a Part.

local i: BasePart = Instance.new("Part")
if typeof(i) == "Instance" then
    print(i.Anchored) -- expected result: false
end
1 Like

If using the new type annotations and strict mode will yield performance improvements in the future then I really hope by then there’s a solution to elegantly converting OOP. Right now converting plain Lua OOP to Luau is tricky and I have yet to find an interim solution that scales well and doesn’t involve things like:

type ClassName = typeof(Class.new())

This is not performant or elegant code especially if the constructor of the class does more than just initialising some members, Also what if the constructor takes args such as an instance of another class in order to create the instance? It’s just wasting CPU time.

2 Likes

In my example

--!strict
local i:Part? = Instance.new"Part"
if typeof(i) == "Instance" then
	print(i.Anchored) -- W000: Key 'Anchored' not found in class 'Instance'
end

i has two possibilities:
nil
Part

If i is an instance then it has to be a Part, the only other possibility doesn’t satisfy typeof(i) == "Instance"

i must be a Part or nil

--!strict
local i:Part? = Instance.new"Part"
i = Instance.new"Frame" -- W000: Type 'Frame' could not be converted into 'Part | nil'
1 Like

TBH not interested in typed Lua at all, I like Lua as it is. But if types are gonna be critical to big performance gains down the road, that’s all you needed to tell me chief.

2 Likes

How much performance do you think this will give once it is fully released? Also does it matter what type check you use even if you type check every variable?

Will there ever be support for custom types? Maybe square brackets will specify that the type variable will be a custom type. You would then specify a tuple which would include the original types inside of the square brackets. It could theoretically look something like this:

local boolAndNumber = [boolean, number]

function TypeFunction(param: boolAndNumber)
 return param
end
print(TypeFunction(39))
--// Which this should promptly print 39

This would allow for more customizable code and a way to better filter out unwanted variables. In the future maybe the type checker will ignore a variable that doesn’t correspond with the type declared.

PS: What’s up with export? I always see it in the Beta version of Studio but I never figured out what it did. Is it unfinished? If so, why add a keyword that doesn’t work yet?

Custom types do exist via the type keyword:

type A = {x: number, y: number, z: number?}
type B = {x: number, y: number, z: number}

local a1: A = {x = 1, y = 2}        -- ok
local b1: B = {x = 1, y = 2, z = 3} -- ok

local a2: A = b1 -- ok
local b2: B = a1 -- not ok

By the way, I got this code from Type checking - Luau. You could also go there to further learn about types and luau in general.

2 Likes

Will there be a way to enable real-time studio-only type assertions? I’d like to get a warning when I assign a variable to the wrong type, but I wouldn’t want it to slow down the live game.

Also, is there a standard way to define a basic “table” or “function” type? I have a lot of code that uses both the array/hash/metatable parts for various things, but I still want to define it as a table. I sometimes get warnings when I use “{}” (I don’t have an example right now.)

There’s also a stringless-object pattern I use everywhere in my game to reduce memory both per-script and per-table. It would be nice to have some support for stringless classes stored as arrays:

local key_Health = 1
local key_MaxHealth = 2
type HealthState = {[1]: number, [2]: number}

In other development environments it’s possible to have class member debug info stripped from your compiled source code (because everything is explicitly typed.) Unless something like that is made possible I’m going to continue doing this; Having strings in your code for each member in a class is equivalent to storing variable names in your script’s debug data.
It’s insignificant for small projects, but it really adds up for massive codebases with tons of unique member names.

9 Likes