Struggle w/ Variadic Generic Types for Classes

The code provided to review is in Typed Luau with an example of how to use it.

The approach I am using has restrictions, such as that extend can only keep the types until the fourth .extend in this case and that the variadic types intended to be used as the constructor for the class don’t pass to the next class past the first pass. The point of the review is primarily to gain insight and hopefully reduce these restrictions as I am not fully satisfied with this approach.

There are many more restrictions, however they are workable. Such as the public, static, method, and private property types responding to the first value each property is set to. That each of these properties is passed to the next class via the extends method and these properties are stored within the type, until the fourth in this case, which can only use the types of the first 3 classes. And more.

Supporting Types
local a = function() end
local b = {}
type Meta = nil
type Feature = nil

type RawObject<B,C> = B & C
type Object<A,B,C,D,E...> = typeof(setmetatable({},
{} :: {
	static: A,
	method: B,
	public: C,
	private: D,
	constr: (E...)->RawObject<B,C>
}
)) & RawObject<B,C>

type Creator<A,B,C,D,E...> = {
	static: A, method: B, public: C, private: D,
	SetConstructor: (
		self: Creator<A,B,C,D,E...>,
		constr: (self: Object<A,B,C,D,E...>, 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,
	SetConstructor: (
		self: ExtendCreator<A,B,C,D,E...,J...>,
		constr: (self: Object<A,B,C,D,E...> & {
			super: Super<E...>
		}, J...)->()
	) -> ()
}

In the code provided I have made it so the variadic types don’t cause an issue with the autocomplete but I have commented what I wish for the autocomplete to act like in the below code and in the example.

type Class3<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,E...,J...>,
		meta: Meta,
		feature: Feature
			  -- Should be J... v
		)->())->Class3<A,B,C,D,E...>
}
type Class2<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,E...,J...>,
		meta: Meta,
		feature: Feature
							  -- Should be J... v
		)->())->Class3<F & A,G & B,H & C,I & D,E...>
}
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,E...,J...>,
		meta: Meta,
		feature: Feature
							  -- Should be J... v
		)->())->Class2<F & A,G & B,H & C,I & D,E...>
}

type New = <A,B,C,D,E...>(name: string, func: (
	creator: Creator<A,B,C,D,E...>,
	meta: Meta,
	feature: Feature
)->()) -> Class1<A,B,C,D,E...>
Example
local a = nil :: New
a("",function(creator, meta, feature)
	creator.public = {a = 1}
	creator.private = {b = 1}
	creator.method = {c = 1}
	creator.static = {d = 1}
	creator:SetConstructor(function(self, h: string, i: number)
		-- For verifying that metatable autocomplete still functions properly
		--getmetatable(self)
	end)
end).extend("", function(creator, meta, feature: Feature)
	creator.public = {e = 1}
	creator.private = {f = 1}
	creator.method = {g = 1}
	creator.static = {h = 1}
	creator:SetConstructor(function(self, j: Instance, k: string) 
		self:super() -- Is: super(string, number)
	end)
end).extend("", function(creator, meta: nil, feature: nil) 
	creator.public = {i = 1}
	creator.private = {j = 1}
	creator.method = {k = 1}
	creator.static = {l = 1}
	creator:SetConstructor(function(self, a1: string, a2: number) 
		self:super() -- Want it to be: super(Instance, string)
	end)
end) -- Want .extend().extend()... to work similarly to those above, constantly expanding the types
-- Instead it stagnates

My first post, but hopefully it is in the right category, tried to justify it. Thanks!

I have made it so that the variadic types pass correctly now. In the Object type I had to remove constr from the metatable, which is fine since I didn’t have any intent to directly use it.

-- Instead of having two variadics, we only have one now** [THIS is why it works]
type ExtendCreator<A,B,C,D,E,J...> = {
	static: A, method: B, public: C, private: D,
	SetConstructor: (
		self: ExtendCreator<A,B,C,D,E,J...>,
		constr: (self: Object<A,B,C,D,E> & {
			super: E
		}, J...)->()
	) -> ()
}
-- Copy the below format for the other Classes
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,
		feature: Feature,
		super: B
		
		)->())->Class2<F & A,G & B,H & C,I & D,J...>
}

Any feedback would be greatly appreciated.

1 Like