OOP Type checking with linked list error

I tried to link with connection for my custom event but it got error saying “Expected type table, got {…} instead.” I tried different methods but it didn’t worked.
For context: Using “LinkedList” for a purpose to call :Disconnect() to remove without loops(Improving performance).

--!strict
-- This is unfinished btw.
local module = {}

local Signal = {} :: SignImpl
Signal.__index = Signal

local Connection = {} :: ConnImpl
Connection.__index = Connection

type SignImpl = {
	__index : SignImpl,
	new: () -> Signal,
	Previous : Signal | Connection,
	Next : Signal | Connection,
	Connect : (self:Signal, Func:(...any) -> ...any, ...any) -> ...any
}

type ConnImpl = {
	__index : ConnImpl,
	new: () -> Connection,
	Previous : Connection | Signal,
	Next : Connection | Signal,
	Disconnect : (self:Connection) -> ()
}

export type Signal = typeof(setmetatable({} :: {
	Previous : Signal? | Connection?,
	Next : Signal? | Connection?
}, {} :: SignImpl))
export type Connection = typeof(setmetatable({} :: {
	Previous : Signal? | Connection?,
	Next : Signal? | Connection?
}, {} :: ConnImpl))

function module.new()
	local s = Signal.new()
	s.Previous = s
	s.Next = s
	return s
end

function Signal.new()
	return setmetatable({}, Signal)
end

function Connection.new()
	return setmetatable({}, Connection)
end

function Signal:Connect()
	local c = Connection.new()
	c.Previous = self.Previous
	c.Next = self
	self.Previous.Next = c
	self.Previous = c
	return c
end

Help me find a way to fix this error.

2 Likes

The thing is Previous and Next can be nil:

    Previous : Signal? | Connection?,
    Next : Signal? | Connection?

before using them, you should check them:

    if self.Previous then
        self.Previous.Next = c
    end

Dunno, looks a little messy as for me.
I’ve got too many "why?"s here, so I did some cleanup, please check this out:

--!strict

local module = {}

type link = {
	next : link?;
	previous : link?;
}

type connection = link & {
	disconnect : (self : connection) -> ();
}

type signal = link & {
	connect : (self : signal) -> connection;
}

local function disconnect(self : connection)
	-- TODO
end

local function new_connection() : connection
	local connection : connection = {
		disconnect = disconnect;
	}
	return connection
end

local function connect(self : signal) : connection
	local connection = new_connection()
	connection.previous = self.previous
	connection.next = self
	if self.previous then
		self.previous.next = connection
	end
	self.previous = connection
	return connection
end

local function new_signal() : signal
	local signal : signal = {
		connect = connect;
	}
	return signal
end

function module.new() : signal
	local signal = new_signal()
	signal.next = signal
	signal.previous = signal
	return signal
end

return module

I advise you against having or using any kind of “signal” implementation in a static game that does not support modding/loadable content.

Also avoid prototype OOP and use ECS instead.

You need to seriously consider debloating the code fast, or else it will be a nightmare for performance.

--!strict
-- This is unfinished btw.
local module = {}

local Signal = {} :: SignImpl
Signal.__index = Signal

local Connection = {} :: ConnImpl
Connection.__index = Connection

type SignImpl = {
	__index : SignImpl,
	new: () -> Signal,
	Previous : Signal | Connection,
	Next : Signal | Connection,
	Connect : (self:Signal, Func:(...any) -> ...any, ...any) -> ...any
}

type ConnImpl = {
	__index : ConnImpl,
	new: () -> Connection,
	Previous : Connection | Signal,
	Next : Connection | Signal,
	Disconnect : (self:Connection) -> ()
}

export type base = {
	Previous : Signal? | Connection?,
	Next : Signal? | Connection?
}

export type Signal = typeof(setmetatable({} :: base, {} :: SignImpl))
export type Connection = typeof(setmetatable({} :: base, {} :: ConnImpl))

local function module_new()
	local s = Signal.new()
	s.Previous = s
	s.Next = s
	return s
end

local function Signal_new()
	return setmetatable({}, Signal)
end

local function Connection_new():Connection
	return setmetatable({}, Connection)::Connection
end

function Signal:Connect()
	local c = Connection_new()
	c.Previous = self.Previous
	c.Next = self
	;(self.Previous::base).Next = c::Connection
	self.Previous = c
	return c
end
1 Like

Sorry for long response.
Yea, those Previous and Next is purpose to not get error but if self.Previous then self.Previous.Next = c end has type error.

Oh wow, you didn’t put metatable on it? I mean its ok. But it works.

local function disconnect(self : connection)
	-- self.Previous.Next = self.next (Type Error: It could be a nil.)
	-- self.Next.Previous = self.Previous (Type Error: It could be a nil.)
	if self.previous then
		self.previous.next = self.next
	end
	if self.next then
		self.next.previous = self.previous
	end
end

Yea, you ask me why. Its kinda my style or method of coding or something because I tried making a new game but idk what my style/method of the coding. You may judge me on it if Im wrong because Im struggling on type annotations.

It has type error because “Its unrelated”. Yea, you tried to fix my code so you tried your best here. Ill find a way to fix it if I can.

You sure?
I defined type base in code snippet i attached.

Uhh… What do you mean you defined type base?

I hate how devforum forces you a char limit.
So yeah i have to yap something or else devforum auto moderation does 1984