This only occurs with tables that have metatables. The examples use variables within a method, but its also the same for when using methods and seems to also affect complex functions that heavily involve the same operations as in the examples.
--!strict
local Class = {}
Class.__index = Class
type Class = setmetatable<{
A: number
}, typeof(Class)>
function Class.Foo(x: Class, y: Class)
local z: Class = setmetatable({
A = 1
}, Class)
if y == z then -- This could also be replaced with an 'assert' function which yields the same bug
return
end
local a = x.A -- Correctly inferred as 'number'
local b = y.A -- Incorrectly inferred as 'any'
local c = (z :: Class).A -- Correctly inferred as 'number' but with forced type casting
end
Strangely, if a member of the table has been previously stored before the table comparison, only that member will be correctly inferred after comparison.
type Class = setmetatable<{
A: {X: number},
B: string
}, typeof(Class)>
function Class.Foo(self: Class)
local a = self.A -- Member previously stored before table comparison
if self == self then
return
end
local z = self.B -- Incorrectly infers as 'any'
local y = self.A -- Correctly infers as '{X: number}'
local x = y.X -- Correctly infers as 'number'
end
The intellisense shows suggestions when trying to access member tables from the instance itself and fully recognizes them, but when accessing from a stored member variable (e.g. a table)—since they’re inferred as ‘any’ type, —no suggestions will be shown.
type Class = setmetatable<{
A: {X: number}
}, typeof(Class)>
function Class.Foo(self: Class)
if self == self then
return
end
local t = self.A. -- Shows that 'X' exists within the suggestions
local v = self.A -- 'A' is Stored as type 'any'
local u = v. -- No suggestions shown
end
This has been a constant problem within a huge modulescript I’m working on which heavily utilizes native code generation and I’m forced into using type casts to make the type solver forcefully recognize the types of each member variables, but I thought that was messy and I figured excessive typecasting—especially the ones within loops—could slow down performance when trying to write performance-critical code.