Typechecking incorrectly invalidating mixed table

Reproduction Steps
To reproduce this issue, create three tables: an array-like table, a dictionary and a mixed table (table that has elements of both styles). Give all of these tables the same type. To start make sure the type is set up for an array-like table. Do not use the special shorthand syntax as the key type needs to be changed to reproduce the issue.

You only need the mixed table to reproduce the issue so the array-like table and dictionary are optional but they give a point of reference when observing how the type behaves with other table types.

Your code should look like the following:

--!strict

type MyType = {[number]: any?}

local array: MyType = {
	1, 2, 3, 4, 5
}

local dictionary: MyType = {
	["foobar"] = "hello",
	["barfoo"] = "world"
}

local mixed: MyType = {
	1, 2, 3, 4, 5,
	["foobar"] = "hello",
	["barfoo"] = "world",
}

To reproduce the bug, change the key type from number to string.

Expected Behavior
If my understanding of typechecking is correct then the two string indices of mixed should be flagged when the key type of MyType is number and they should be valid when the key type is string.

Actual Behavior
When the key type of MyType is number, the mixed table including its string indices is recognised as acceptable. When the key type of MyType is string, the whole mixed table including the string indices are flagged. The array and dictionary tables behave as expected and get flagged respectively when the key type does not match. Below is media demonstrating the issue.

Up0IJ8xqM1

Workaround
Union typing: accepting a union between number and string will not flag any of the tables as they all successfully meet the criteria.

type MyType = {[number | string]: any?}

Issue Area: Studio
Issue Type: Other
Impact: Low
Frequency: Constantly
Date First Experienced: 2021-05-31 21:09:00 (-04:00)
Date Last Experienced: 2021-06-02 03:00:00 (-04:00)

1 Like

Try removing the ? operator. I remember wasting about 30 minutes trying to figure out why my function with an optional argument threw an error and I just fixed it by replacing ? with any.

Results are the same. Whether the ? operator is on the key, value, both or none the issue is reproducible. When I originally encountered this bug I had ? on both the key and value; when typing up this report I ditched the ? on the key type assuming that’d make a difference.

What’s going on is a bit of subtyping.

--!strict
-- A more accurate type for mixed
type MixedType = {[number]: number, foobar: string, barfoo: string}
type MyType = {[number]: any?}

-- Declare mixed at its most accurate type
local mixed: MixedType = {
	1, 2, 3, 4, 5,
	["foobar"] = "hello",
	["barfoo"] = "world",
}

-- And then upcast it, which is okay as MixedType is a subtype of MyType
local mine: MyType = mixed

The ergonomics of this aren’t perfect though, as you’ve discovered. Trying to combine property-based and index-based access to the same table produces some weird results!

For reference: the endgoal for me is mainly to restrict tables to a certain way I specify. I originally caught this issue while making an Array type which is where MyType comes from/is formatted as, hoping that Script Analysis would flag non-numerical indices. I don’t actually want a mixed table. Would there be a way to do that or does this subtyping behaviour make that impossible?

At the moment I don’t think there’s a way to switch off this subtyping, though it only is an issue when the table is first being created, you can’t later add properties. Mixed tables are tricky, as some code relies on them, so the typechecker has to support them, but they’re often not what you want.

1 Like

Hello! Does this issue still occur for you anymore?; or we can close the issue… Thank you!

Thank you for checking back in. The issue in the OP is still occurring but in a different vein: when I change the key type to string, numerical indices do not get flagged. Not sure if I should file that as a separate issue since it doesn’t appear to be the same as the one I wrote in the OP.

image

Using number as my key type still correctly validates as it originally did but the inverse is happening with string keys wherein all tables become valid instead of invalid like the original issue.

Yes, this is not optimal. Mixing named properties with indexers in the same table is problematic.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.