Luau Type Checking Beta!

But this casting really should not be necessary because even though it could potentially not return something, it should assume that in this case, this is what I expect, especially since I have tried checking with if statements too. This should be the same case for other optional returns as well. And if you give it to a variable with a specific type like Instance, it should expect that type as well and not warn. This is the same type of behavior found with JSDocs as I mentioned above.

Yeah I’m aware of that. But in the case of use, it’s using namecall meaning that it should say that there are only 2 required arguments and not 3.

1 Like

TypeScript does a similar sort of thing - unless you explicitly tell it “hey, this will always be an Instance”, it doesn’t have a clue most of the time when dealing with multiple return types. It can’t figure out that what you are doing will always return an Instance by itself, which is the point of giving it multiple return types in the first place. You have to give it a little bit of help. A lot of strongly typed languages do this as well, e.g.:

// some random code as an example
if (sender instanceof Player) {
    Player p = (Player)sender;
}

As much as we want a perfect world with perfect type checking that knows exactly what we want, we can’t get it just yet. I will imagine Luau will analyze if statements in the future to try and determine types but we will have to see.

It is very intentional that Luau flag cases where you try to call methods on something that might be nil.

The whole point is to help you find these kinds of bugs before you run the code. :slight_smile:

The warning should go away if you first check that it succeeded:

local root = recursiveFind(script, "Songblocks")
if typeof(root) == "Instance" then
    module.Songblocks = root
    module.Main = root:WaitForChild("Main")
else
    error("Unexpected: Could not find Songblocks node!")
end

Pardon if this was already asked, but any rough estimation on when type checking will come out of beta so it can be used in production code?

3 Likes

Getting an error indexing a CFrame with the following code:

how to get the local player in 2020

--!strict

type userdata = {[string]: any?}; -- hehea boi

local _ : boolean, player : userdata? = pcall(function(input : any?) => userdata? | nil return game.Players.LocalPlayer end) 

print(player) → local player

5 Likes

Is there a way to set types of tables in other tables?
image

Nevermind, figured out how to fix it:
image

1 Like

https://i.gyazo.com/8e81a7b8b67695a6289160dadbd0a685.mp4

This warning doesn’t make any sense.

is there a way to silence this it’s annoying

The second and third parameters are meant to be given as enum items or not given (EnumItem | nil), not numbers.

TweenInfo.new ( number time = 1.0, Enum easingStyle = Enum.EasingStyle.Quad, Enum easingDirection = Enum.EasingDirection.Out, …)

Your code might work because internally it coerces the numbers into the right value, but this is still a type mismatch compared to the API signature.

6 Likes

Worth adding is that we consider various coercions in the language a design mistake - part of it Lua’s (string<->number), part of it ours (string->Enum, number->Enum, possibly others).

While we can’t “fix” this because that would break existing code, we discourage these coercions as they make code harder to reason about and occasionally result in surprising errors, and as such our type system doesn’t implement them.

6 Likes

Should there be a warning for types that are impossible to instantiate?

type Foo = {Bar: Foo}

To create an object of type Foo, you must first have an object of type Foo, which isn’t possible.

type Foo = {Bar: Foo}
local foo = {}
foo.Bar = foo
local bar: Foo = foo

(it’s slightly magical but it works)

Isn’t that kind of hacky though?

Okay now I’m a bit skeptical. How would the VM do type assertions/optimizations for cases like this?

type Foo = {Bar: Foo}
local foo = {}
foo.Bar = foo

delay(1, function()
    foo.Bar = 123 -- Make 'bar' below no longer a valid 'Foo'
end)

local bar: Foo = foo

print(bar.Bar)
-- > table: 0xfff38e6a129bf91e

wait(2)
print(bar.Bar)
-- > 123

I assumed table types would have a special “optimized” internal representation, and would be type safe. I’m 100% in support of type checking for all Lua/Roblox types, but I’m not sure about table typing.


Is the type system just for maintainability, or are there also VM optimizations planned?

Say you have a function in a module:

local module = {}
function module.Foo(a: number, b: number, c: number) => bool
	-- a, b, and c are of type number, so the VM should be able to optimize these instructions.
	return a^2 + b^2 == c^2 -- test for pythagorean triple
end
return module

Calling a function with known input/output types can be optimized by the VM potentially, so it could be required like this:

local module = require(script.Module)

-- This line should make the assertion that foo is of this type so that calling Foo can be optimized.
local Foo: (number, number, number) => bool = module.Foo

for a = 1, 16 do
	for b = 1, 16 do
		for c = 1, 16 do
			-- a, b, and c are all of type number, so passing arguments to Foo should require no type checking
			if Foo(a, b, c) then
				print("Found!", a, b, c)
			end
		end
	end
end

Will this level of optimization and type safety be possible?

2 Likes

I agree that using the number makes it less readable and less accessible.

@zeuxcg, @buildthomas

Thanks for letting me know.

1 Like

Note that your example doesn’t typecheck in strict mode. The number one goal of the type system right now is to increase robustness - not to increase performance. Once we’re happy with there that is at, we’ll start taking a look at type-directed optimizations in the compiler/VM - these will require more care on the implementation front, and definitely would assume strict mode. (strict mode isn’t quite enough though)

2 Likes

Fun fact: Setting part.Material to Enum.Material.Grass is roughly 10% faster than setting it to the corresponding value, 1280, and substantially faster than the corresponding name "Grass".


(this is after 80m iterations on my Galaxy Note 8)

There’s really no reason to use EnumValue.Value instead (unless you want your code to use less memory I suppose.)

3 Likes

Getting an error when indexing properties of objects who the type checker thinks are Instances:

image

This seems to be a difficult problem to solve. Maybe just suppress error messages for Instances? (If that isn’t too terrible an idea lmao)

1 Like