Major Luau type system regression when using "any" as an index type or union value types

Reproduction Steps

The following snippet of code emits a warning:

--!strict
type MyRecord = {[any]: string | boolean}

local x: MyRecord = {
	Foo = "Fighters",
}

Warning:

Type Error: (4,1) Type ‘x’ could not be converted into ‘MyRecord’
caused by:
Property ‘Foo’ is not compatible. Type ‘(boolean | string)?’ could not be converted into ‘string’
caused by:
Not all union options are compatible. Type ‘boolean’ could not be converted into ‘string’

However, changing the index type to “string” removes this warning:

--!strict
type MyRecord = {[string]: string | boolean}

local x: MyRecord = {
	Foo = "Fighters",
}

This warning causes nearly every piece of code using the Pract Library, as it has a very commonly-used type with an any index (These keys are converted to a string internally) and a union type for values

Expected Behavior

Luau used to be able to infer types like this, and putting a type annotation on the variable at-declaration used to coerce the initializing value with little friction.

It seems Luau still does this for types that have a string index type, or a simple value type (i.e. the values are not a union of types)

Actual Behavior

Instead, this causes warnings for otherwise perfectly benign code. This means that any developers using my Pract library will see a bunch of script analysis warnings for what used to be perfectly valid code.

Workaround

The only workaround I’ve found so far is to initialize a table as empty, then add children gradually. In the first example:

--!strict
type MyRecord = {[any]: string | boolean}

local x: MyRecord = {}
x.Foo = "Fighters"

This is not a feasible refactor for Pract code, as it requires converting a simple expression into multiple statements + a variable declaration. Working around this issue requires a very destructive refactor for a developer-facing library.

An alternative workaround is to type all index types as string, but this will also break existing Pract code, because sometimes it is convenient to create children as follows (using numbers as keys rather than strings):

-- "children" is typed as {[any]: Pract.Element | boolean | nil}
-- "Pract.Element" is typed as {[any]: any}, and is similar to a React element
local children: Pract.ChildrenArgument = {}
for i = 1, 10 do
	table.insert(
		children,
		Pract.create(
			"Frame",
			{
				Position = UDim2.fromScale(0.5, 0.5),
				AnchorPoint = Vector2.new(0.5, 0.5),
			}
		)
	)
end
return Pract.create("Frame", {}, children) 

Issue Area: Studio
Issue Type: Other
Impact: Very High
Frequency: Constantly
Date First Experienced: 2022-05-15 00:05:00 (-06:00)
Date Last Experienced: 2022-05-15 00:05:00 (-06:00)

1 Like

Thank you for the report.
We have found the recent change that caused this error to be reported. We will probably disable it until we fix this case.

2 Likes

Fix for this has been implemented, it should arrive in an update next week.
Problematic feature hasn’t been disabled yet, we are still looking into it.

2 Likes

I legitimately can’t get any work done when there’s 193 warnings in my codebase, especially since it affects almost all UI code. What I’m working on is super UI heavy

It’s really hard to write code when the entire code editor looks like this:

In my case, I can probably fork the Pract library and use the “any” type for this. I will need to make a bunch of searchable comments to remember to undo the any typings though.

Feature that caused the issue has been disabled. Sorry for the trouble.

2 Likes