TyperChecker2 fix which ended up as UnionType Table Indexer Support

This commit is contained in:
karl-police 2024-09-16 03:00:46 +02:00
parent e8a7acb802
commit 9f94c49674
2 changed files with 133 additions and 0 deletions

View file

@ -3015,6 +3015,60 @@ PropertyType TypeChecker2::hasIndexTypeFromType(
if (isPrim(indexType, PrimitiveType::String))
return {NormalizationResult::True, {tt->indexer->indexResultType}};
// If the indexer looks like { [any] : _} - the prop lookup should be allowed!
else if (auto indexTy = get<UnionType>(indexType))
{
// If the Indexer is a UnionType
// TODO: This is looping everytime, on every request, through everything?
// That doesn't sound good. But I am not sure if the other parts do that as well.
auto visitOption = [&](TypeId& option, const std::string& prop) -> PropertyType
{
if (auto tySingleton = get<SingletonType>(option))
if (auto ty = get<StringSingleton>(tySingleton))
if (ty->value == prop)
return {NormalizationResult::True, {tt->indexer->indexResultType}};
// If all fails
return {NormalizationResult::False, {}};
};
auto visitUnionOptions = [&](UnionType unionTy, const std::string& prop) -> PropertyType
{
for (TypeId option : unionTy.options)
{
option = follow(option);
// No recurse
if (get<UnionType>(option))
return {NormalizationResult::False, {}};
PropertyType result = visitOption(option, prop);
if (result.present == NormalizationResult::False)
continue; // Continue searching if it failed
// Otherwise return
return result;
}
// If all fails
return {NormalizationResult::False, {}};
};
for (TypeId option : indexTy->options)
{
option = follow(option);
if (auto unionTy = get<UnionType>(option))
{
PropertyType result = visitUnionOptions(*unionTy, prop);
if (result.present == NormalizationResult::False)
continue; // Continue searching if it failed
else
return result; // Otherwise return
}
return visitOption(option, prop);
}
}
else if (get<AnyType>(indexType) || get<UnknownType>(indexType))
return {NormalizationResult::True, {tt->indexer->indexResultType}};
}

View file

@ -690,6 +690,85 @@ TEST_CASE_FIXTURE(ClassFixture, "vector2_multiply_is_overloaded")
CHECK("mul<Vector2, string>" == toString(requireType("v4")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "union_used_as_table_indexer_assignProp_no_error")
{
if (!FFlag::LuauSolverV2)
return;
CheckResult check1 = check(R"(
--!strict
type NumberTable = {
One: number,
Two: number,
Three: number
}
type Indexables = "One" | "Two" | "Three" | typeof(5)
type Test = {
TestKey: number,
[Indexables | "Four"]: number
}
local tbl = {} :: Test
-- This was just used to compare it with index
-- type TestIndex = index<typeof(tbl), "Three">
tbl["Three"] = 3;
tbl.Three = 3;
tbl.Four = 4;
tbl.TestKey = 5;
)");
// It should be possible to do "tbl.Three" without erroring.
LUAU_REQUIRE_NO_ERRORS(check1);
// auto test1 = requireTypeAlias("TestIndex");
// auto test2 = requireType("tbl");
// TODO: This doesn't include the autocomplete fix
// That has to be still made.
// It is supposed to include the indexes from the UnionType
// but it is possible to have more than just that
// It's also to note that strings may not be the only indexable things
// auto ac = autocomplete('1');
}
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_used_as_table_indexer_assignProp_no_error")
{
if (!FFlag::LuauSolverV2)
return;
CheckResult check1 = check(R"(
--!strict
type NumberTable = {
One: number,
Two: number,
Three: number
}
type Indexables = keyof<NumberTable>
type Test = {
TestKey: number,
[Indexables | "Four"]: number
}
local tbl = {} :: Test
tbl["Three"] = 3;
tbl.Three = 3;
tbl.Four = 4;
tbl.TestKey = 5;
)");
// It should be possible to do "tbl.Three" without erroring.
LUAU_REQUIRE_NO_ERRORS(check1);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_rfc_example")
{
if (!FFlag::LuauSolverV2)