Metatable type defect with merged table types (&)

Non-validating code:

--!strict

export type abstract_raw_schema = {
	_foo: number,
}

export type abstract_index_schema = {
	Bar: (abstract_schema ) -> (),
}
export type abstract_schema = typeof(setmetatable({}::abstract_raw_schema, {__index={}::abstract_index_schema}))

export type normal_raw_schema = abstract_raw_schema & {
	_baz: number,
}
export type normal_index_schema_tmp = {
	Qux: (normal_schema ) -> (),
}
export type normal_index_schema = typeof(setmetatable({}::normal_index_schema_tmp, {__index={}::abstract_index_schema}))
export type normal_schema = typeof(setmetatable({}::normal_raw_schema, {__index=({}::any)::normal_index_schema}))

local t: normal_schema = nil -- just for testing
-- PROBLEM: t:Bar() & t:Qux() shows as non-existant!
-- culprit is that normal_raw_schema consists of abstract_raw_schema & something else. works fine if I just use 'abstract_raw_schema'

Validating code but with another defect:

--!strict

export type abstract_raw_schema = {
	_foo: number,
}

export type abstract_index_schema = {
	Bar: (abstract_schema) -> (),
}
export type abstract_schema = typeof(setmetatable({}::abstract_raw_schema, {__index={}::abstract_index_schema}))

export type normal_raw_schema = abstract_raw_schema --[[& {
	_baz: number,
}]]
export type normal_index_schema_tmp = {
	Qux: (normal_schema) -> (),
}
export type normal_index_schema = typeof(setmetatable({}::normal_index_schema_tmp, {__index={}::abstract_index_schema}))
export type normal_schema = typeof(setmetatable({}::normal_raw_schema, {__index=({}::any)::normal_index_schema}))

local t: normal_schema = nil -- just for testing
t:Qux() -- validates and works fine now!
t:Bar() -- does not show up in intellisense also does not validate!

Working example:

--!strict

local module = {}


--- Abstract types/implementation
export type abstract_raw_schema = {
	_foo: number,
}

export type abstract_index_schema = {
	Bar: (self: abstract_schema) -> (),
}
export type abstract_schema = typeof(setmetatable({}::abstract_raw_schema, {__index={}::abstract_index_schema}))

local abstract_prototype = {}
function abstract_prototype.Bar(self: abstract_schema)
	print("Bar")
end


--- Normal types/implementation
export type normal_raw_schema = abstract_raw_schema & {
	_baz: number,
}
export type normal_index_schema_tmp = {
	Qux: (self: normal_schema) -> (),
}
export type normal_index_schema = typeof(setmetatable({}::normal_index_schema_tmp, {__index={}::abstract_index_schema}))
export type normal_schema = typeof(setmetatable({}::normal_raw_schema, {__index=({}::any)::normal_index_schema}))

local normal_prototype = {}
setmetatable(normal_prototype, {__index=abstract_prototype})
function normal_prototype.Qux(self: normal_schema)
	print("Qux")
end


--- Test
local self: normal_raw_schema = {
	-- Abstract
	_foo = 1,
	-- Normal
	_baz = 2,
}
local self: normal_schema = setmetatable(self, {__index=normal_prototype})
self:Qux()  -- does not validate, but works
self:Bar() --  does not validate, but works

return module
1 Like

Thanks for the report.We’ll follow up when we have an update for you.

1 Like

Thanks for the report! The underlying issue here is that the old type solver is not generally capable of normalizing types to the extent possible to make your chain of operations work. In the New Type Solver, we’ve improved this by increasing a lot of the rigor around intersections and metatables generally. We’ve also added a setmetatable type function that enables you to clean up the code a fair bit.

export type abstract_raw_schema = {
	_foo: number,
}

export type abstract_index_schema = {
	Bar: (abstract_schema ) -> (),
}
export type abstract_schema = setmetatable<abstract_raw_schema, abstract_index_schema>

export type normal_raw_schema = abstract_raw_schema & {
	_baz: number,
}
export type normal_index_schema_tmp = {
	Qux: (normal_schema ) -> (),
}
export type normal_index_schema = setmetatable<normal_index_schema_tmp, { __index: abstract_index_schema }>
export type normal_schema = setmetatable<normal_raw_schema, { __index: normal_index_schema }>

local t: normal_schema = nil :: any -- just for testing
t:Qux()
t.Bar(t :: abstract_schema)

1 Like

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