Issue with generic class typing

I’m trying to write a module for a generic queue data structure with full type definitions but I can’t figure out how to structure the type descriptions.

This is the closest I’ve gotten:

export type QueueImpl = {
	__index: QueueImpl,
	new: <T>() -> Queue<T>,
}

export type Queue<T> = typeof(setmetatable({} :: {}, {} :: QueueImpl))

Which gives a TypeError: Recursive type being used with different parameters error on the third line.

Is there a way to structure this that would give the proper type definitions?

1 Like

Okay I found a solution and I’ll post it here for the next person who has a similar issue.

I’ll post the full class definition since there’s not that much and it illustrates this typing style better:

type self<T> = {
	Contents: { T },
	First: number,
	Last: number,
}

local Queue = {}
Queue.__index = Queue

export type Queue<T> = typeof(setmetatable({} :: self<T>, Queue))

function Queue.new<T>(): Queue<T>
	local self = setmetatable({} :: self<T>, Queue)
	self.Contents = {}
	self.First = 1
	self.Last = 0
	return self
end

function Queue.enqueue<T>(self: Queue<T>, item: T): ()
	self.Last += 1
	self.Contents[self.Last] = item
end

function Queue.dequeue<T>(self: Queue<T>): T
	if self.First > self.Last then
		error("Attempted to remove from an empty queue")
	end

	local item = self.Contents[self.First]
	self.Contents[self.First] = nil
	self.First += 1
	return item
end

function Queue.len<T>(self: Queue<T>): number
	return self.Last - self.First + 1
end

return Queue

Basically, instead of creating the type definition ahead of time, this style is dynamically generating it from the type definitions of the functions.

This style does require writing member functions a bit awkwardly (explicitly defining the type of self in each function definition) but overall I rather prefer it to the “header definitions” style from before.

2 Likes

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