Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Interfaces and Implementors #3150

Open
Perodactyl opened this issue Apr 4, 2025 · 1 comment
Open

[Feature Request] Interfaces and Implementors #3150

Perodactyl opened this issue Apr 4, 2025 · 1 comment

Comments

@Perodactyl
Copy link

Perodactyl commented Apr 4, 2025

The current class system is alright, but imagine this: I have a Stream class which is extended by ReadStream, WriteStream, SeekableStream, etc.

---@class Stream
---@field close fun() Deallocates or hangs up a stream.

---@class SeekableStream: Stream
---@field seek fun(whence: SeekWhence, offset: integer): integer

---@class ReadStream: Stream
---@field read fun(self: ReadStream, length: integer): string|nil

---@class WriteStream: Stream
---@field write fun(data: string): integer

---@class StringStream: ReadStream, SeekableStream Streams data out of a string.

Now I have a function which takes any Seekable, Readable string. How do I specify this? Either we need a conjunction type (ReadStream & SeekableStream, which has keys from both), or we need (preferably) interfaces, which are generic and can be extended by classes. The reason I suggest interfaces is because if I write this:

---@param length integer
---@return string|nil
function StringStream:read(length)
	if self.buffer == nil then
		error("Stream is closed", 2)
	end
	if self.cursor > #self.buffer then
		return nil
	end
	local out = string.sub(self.buffer, self.cursor, math.min(#self.buffer, self.cursor + length))
	self.cursor = self.cursor + #out
	return out
end

and I omit the type annotation, it assumes read is already defined because StringStream extends ReadStream, and it doesn't validate types (arguments and return type are any).

@tomlau10
Copy link
Contributor

tomlau10 commented Apr 5, 2025

This is already supported to a certain extend by author in v3.10.6

  • Infer the parameter types of a same-named function in the subclass based on the parameter types in the superclass function.
    ## 3.10.6
    `2024-9-10`
    * `NEW` Custom documentation exporter
    * `NEW` Setting: `Lua.docScriptPath`: Path to a script that overrides `cli.doc.export`, allowing user-specified documentation exporting.
    * `NEW` Infer the parameter types of a same-named function in the subclass based on the parameter types in the superclass function.
  • at commit: 7c481f5

example

---@class ReadStream
local ReadStream

---@param length integer
---@return string|nil
function ReadStream:read(length)    --> length: integer
end

---@class StringStream: ReadStream

---@class StringStream
local StringStream

function StringStream:read(length)  --> length: integer
end

StringStream:read("")   --> Cannot assign `string` to parameter `integer`.

but may be bugged when using @field syntax ...

From your reproduction code snippet, I believe there maybe some bugs when defining the interface using @field syntax 😕
Because when you use @field to define the read() method, no type inference on the implemented method param.

---@class ReadStream
---@field read fun(self: ReadStream, length: integer): string|nil
local ReadStream

---@class StringStream: ReadStream

---@class StringStream
local StringStream

function StringStream:read(length)  --> length: any
end

StringStream:read("")   --> no type checking

current workaround

So the current workaround is to define the interface using function style (maybe you already have a separate @meta file?), instead of @field, before this issue is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants