luau icon indicating copy to clipboard operation
luau copied to clipboard

General Upvalue type guessing - Type error bug in custom type dictionnary assignement

Open paulogarithm opened this issue 8 months ago • 1 comments

Description

luau intellisence fails when you are trying to use a dictionnary value of a custom type:

Example

i tried to simplify the example as much as possible

--!strict

export type T = "foo" | "bar" | "toto"

local object: T = "foo"

local getOpposite: {[T]: T} = {
	["foo"] = "bar",
	["bar"] = "toto",
	["toto"] = "foo"
}

local function hello()
	local x = getOpposite[object] -- intellisence warning

	if x then
		object = x -- intellisence warning
	end
end

Logs

the error is as follows:

Type Error: Type ' "bar"|"foo"|"toto" | ("bar"|"foo"|"toto") & (buffer|class|function|number|string|table|thread|true)) ' could not be converted into ' "bar"|"foo"|"toto" '; this is because the 4th component of the union is ' ("bar"|"foo"|"toto") & (buffer|class|function|number|string|table|thread|true)) ', which is not a subtype of ' "bar"|"foo"|"toto" '.

(i copied it manually, so it can have typos)

Notes

note that you should have the object declaration in order for it to reproduce the issue

...

local function hello()
	local x = getOpposite[object] -- no warning
        print(x)
end

also, note that casting the dictionnary into {[T]: string}, {[string]: T} and even {[any]: any} or any will also fail, but sometimes not on both lines, example bellow with {[any]: any}

...

local getOpposite: {[any]: any} = {
	["foo"] = "bar",
	["bar"] = "toto",
	["toto"] = "foo"
}

local function hello()
	local x = getOpposite[object] -- no warning

	if x then
		object = x -- intellisence warning
	end
end

the only way i had to fix that was to explicit cast types:

...
        local x: T= getOpposite[object::T]

PS: maybe its just me who is dumb, in that case i'm honnestly sorry

paulogarithm avatar Apr 13 '25 21:04 paulogarithm

related to #1768 and @LuauHater post, it seems to be a global issue with upvalue type guessing, because i tried to debug making object variable in the same scope as the definition, and it seems to work fine:

export type T = "foo" | "bar" | "toto"

local getOpposite: {[T]: T} = {
	["foo"] = "bar",
	["bar"] = "toto",
	["toto"] = "foo"
}

local function hello()
	local object: T = "foo"
	local x = getOpposite[object]

	if x then
		object = x
	end
end

paulogarithm avatar Apr 13 '25 21:04 paulogarithm

isolate the error

basically it's just the type guessing that is messy, but on certain conditions, for example when a constant value, a local value and an upvalue is together, it creates the warning:

local function foo()
	local carry = 0
	for i = 0, 10 do
		carry = i + carry + 1 -- warning
	end
end
local function foo()
	local carry = 0
	for i = 0, 10 do
		carry = i + carry -- no warning
                carry = carry + 1 -- no warning
	end
end

for them, the type of the operration is : t1 where t1 = add<number, add<t1, number> | number> and the type of carry is : t1 where t1 = add<add<number, t1>, number> | number

paulogarithm avatar Apr 23 '25 11:04 paulogarithm