[Typed Luau] Metatable __index type not being capable of supporting intersections

This post has two parts, first is clarifying the title and the other is the specific implementation I am attempting to use it in, mainly showing that in some obscure cases, it is, to my current knowledge, necessary to type the __index metamethod rather than just intersecting many types.

PART 1

The autocomplete for my table isn’t working as I expected it to.

type AppleColor = {Color: {R: number, G: number, B: number}}
type AppleSize = {Size: {X: number, Y: number}}
local Apple = setmetatable({}, {
	__index = {} :: (AppleColor & AppleSize)
})

Expected equivalent


Reality

Autocomplete for just one

I do not have any plugins enabled. I tried the new type solver and old one.
I cannot just set the overall type, since this is the simplified version and the actual one involves generics where it is necessary to intersect the types. To my knowledge there is no other approach than using the __index metamethod type to have autocomplete act the way I need for my specific situation.

PART 2

Code so you can prove me wrong ( or learn from it :D )
-- Meta and Feature are hundreds of lines each and they aren't the problem
type Meta<A> = {}
type Feature = {}
type RawObject<B,C> = B & C

type ObjectMeta<A,B,C,D> = {
	__index: B,
	static: A,
	method: B,
	public: C,
	private: D
}

type Object<A,B,C,D> = typeof(
	setmetatable({},{} :: ObjectMeta<A,B,C,D>)
) & C

type Creator<A,B,C,D,E...> = {
	static: A, method: B, public: C, private: D, 
	_selfType: Object<A & {},B & {},C & {},D & {}>,
	SetConstructor: (
		self: Creator<A,B,C,D,E...>,
		constr: (self: Object<A,B,C,D>, E...)->()
	) -> ()
}

type Super<E...> = typeof((nil :: typeof(
	function()
		--[[Super]]--
		function b:super(...: E...)
			if self and a(self) then end
		end
		return b.super
	end
	))())

type ExtendCreator<A,B,C,D,E,J...> = {
	static: A, method: B, public: C, private: D,
	_selfType: Object<A,B,C,D>,
	SetConstructor: (
		self: ExtendCreator<A,B,C,D,E,J...>,
		constr: (self: Object<A,B,C,D> & {
			super: E
		}, J...)->()
	) -> ()
}
-- assume Class2 is the same as Class1, but returns Class2<A,B,C,D,E...> instead
type Class1<A,B,C,D,E...> = A & {
	create: (E...) -> RawObject<B,C>,
	extend: <F,G,H,I,J...>(name: string, func: (
		creator: ExtendCreator<F & A,G & B,H & C,I & D,Super<E...>,J...>,
		meta: Meta<Object<F & A,G & B,H & C,I & D>>,
		feature: Feature,
		super: B

		)->())->Class2<A & F,B & G,H & C,I & D,J...>
}

export type New = <A,B,C,D,E...>(name: string, func: (
	creator: Creator<A,B,C,D,E...>,
	meta: Meta<Object<A,B,C,D>>,
	feature: Feature
	)->()) -> Class1<A,B,C,D,E...>

-- Sample usage code 
local sample: New = nil
local first = sample("First", function(creator, meta, feature) 
	creator.static = {a = 1}
	creator.public = {b = 1}
	-- the below line causes the need for __index (or a substitute)
	type Self = typeof(creator._selfType)
	creator.method = {c = function(self: Self) 
		return self.b 
	end}
	creator.private = {d = function(self: Self,b,c) 
		self.b = c 
	end}
	creator:SetConstructor(function(self, b: number) 
		if self:c() == b then return end
	end)
end)

local second = first.extend("Second", function(creator, meta, feature, super) 
	creator:SetConstructor(function(self, ...) 
		self -- self:c should popup but doesn't
	end)	
end)

Is there any alternative way to achieve the same result without breaking any of the functionality present in the type autocompletion?

1 Like

This seems like a bug to me. You can report it via Issues on the Luau GitHub repository:

3 Likes