Setmetatable's Type-Checking bugs out when using {@ metatable t, m}

Description

The Type-Checking for setmetatable bugs out when you try to use another table with a metatable attached ({@ metatable t, m}), preventing the autocompleter from working properly, especially when using the __index metamethod.

Repro

--!strict

local Foo = {}
Foo.__index = Foo
Foo.A = 1

function new1()
	return setmetatable({}, Foo)
end


local Bar = {}
Bar.__index = Bar
Bar.B = 2

setmetatable(Bar, Foo)

function new2()
	return setmetatable(new1(), Bar) -- 'setmetatable should take a table'
end

print(new2().B) -- Bugged

Additional Info

This is the specific bug that prevents writing Strictly-Typed OOP as it prevents the SubClass type from reading methods from itself in the below examples.

SuperClass (Not Bugged)
--!strict

local SuperClass = {}
SuperClass.__index = SuperClass


type self = {
	SuperValue: number
}

export type SuperClass = typeof(setmetatable({} :: self, SuperClass))


function SuperClass.new(): SuperClass
	local self: SuperClass = setmetatable({} :: self, SuperClass)
	
	self.SuperValue = 1
	
	return self
end

function SuperClass:Foo(): ()
	local self: SuperClass = self
	
	print("Foo")
end


return SuperClass
SubClass
--!strict

local SuperClass = require(SuperClass)

local SubClass = {}
SubClass.__index = SubClass

setmetatable(SubClass, SuperClass)


type self = SuperClass.SuperClass & {
	SubValue: number
}

export type SubClass = typeof(setmetatable({} :: self, SubClass))


function SubClass.new(): SubClass
	local self = setmetatable(SuperClass.new() :: self, SubClass)
	
	self.SuperValue = 1
	self.SubValue = 1
	
	self:Bar() -- Nope
	
	return self
end

function SubClass:Bar(): ()
	local self: SubClass = self
	
	print("Bar")
end

return SubClass
2 Likes

We’ve filed a ticket to our internal database for this issue, and will update you as soon as we have news!

Thanks for flagging!

5 Likes

Checking on this two and a half months later. This bug is still present and interfering with my workflow.

The problem is most likely caused by normal tables not being able to have the type of a table with a metatable casted onto it. This would explain why setmetatable doesn’t like the forementioned type.

--!nonstrict
local Table = {} :: typeof(setmetatable({}, {}))
2 Likes

Hey everyone, wanted to circle back here, is this still an issue?

1 Like

Extremely sorry for the long wait, but yes, the t parameter still doesn’t like having a table with a metatable attached.

I’ve found ways to “trick” analysis into ignoring it, i.e. putting setmetatable at the very bottom of the file, but they are a little unstable.

yes, it is still an issue

asdasdasdasd