[Feature Request] Interfaces and Implementors
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).
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. https://github.com/LuaLS/lua-language-server/blob/802e13ccac1153c32b90e6f203fa482fff7a603e/changelog.md?plain=1#L156-L160
- at commit: https://github.com/LuaLS/lua-language-server/commit/7c481f57c407f7e4d0b359a3cfbce66add99ec2f
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.