You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm running into an issue where adding the ---@class annotation to a local variable seems to break auto completion. It should appear correctly even if the ---@Class is affixed to a local variable.
Actual Behaviour
The autocomplete variables and functions are not appearing.
Reproduction steps
Here is a simple example of creating a new player class with the issue.
-- Here is a simple example of creating a new player class with the issue-- Uncommenting the line below *breaks* the autocompletion for p.new()-- ---@class err.PlayerlocalPlayer= {}
Player.__index=Playerlocalfunctionmixin_xy(o)
o.x=0o.y=0o.set_position=function(self, x, y)
self.x=xself.y=yendreturnoendfunctionPlayer.new()
localself= {}
self.happy=trueself=mixin_xy(self)
returnsetmetatable(self, Player)
endlocalp=Player.new()
-- `p:set_position(), p.x, p.y, p.happy` no longer showing in auto complete if `---@class err.Player` is uncommented
When ---@class err.Player is uncommented, and above local Player = {}:
When ---@class err.Player is removed or commented:
The above image is the expected behaviour. However, when ---@class err.Player is added above local Player = {} it no longer works.
When you use @class, only the fields directly injected into a variable marked by @class, or fields that are defined in method of this variable will be considered as fields of that class.
In you example
---@classPlayerlocalPlayer= {} --<< now this `Player` variable is marked with `@class`Player.__index=Player--<< so `__index` is a field of the classlocalfunctionmixin_xy(o)
-- `o` is just a variable with `Player` type, it is NOT marked with `@class`o.x=0-- so this `x` field is not injected to `Player`o.y=0-- so as `y`o.set_position=function(self, x, y)
self.x=xself.y=yendreturnoendfunctionPlayer.new()
localself= {} -- `self` is just a `table`, not marked with `@class`self.happy=true-- thus `happy` is not injected to `Player`self=mixin_xy(self) -- no effect to `Player` classreturnsetmetatable(self, Player) -- mark `self` as `Player` typeendlocalp=Player.new() -- as you see, none of the code above has effect to `Player` class-- so only the `Player.new` and `Player.__index` is shown here
how to use @class in your example
If you want to use @class, one way is like this
---@classPlayerlocalPlayer= {}
Player.__index=Playerlocalfunctionmixin_xy(o)
---@classPlayerlocalo=o-- need to mark variable `o` as `@class Player`o.x=0-- to let `x` be injected into `Player` classo.y=0-- so as `y`o.set_position=function(self, x, y)
self.x=xself.y=yendreturnoendfunctionPlayer.new()
---@classPlayerlocalself= {} -- need to mark `self` as `@class Player` tooself.happy=trueself=mixin_xy(self)
returnsetmetatable(self, Player)
endlocalp=Player.new() -- now this should work
if your want mixin_xy for more general use
you will have to define it as another class (interface)
---@classPlayerlocalPlayer= {}
Player.__index=Playerlocalfunctionmixin_xy(o)
---@classMixinXYlocalo=oo.x=0-- now `x` is injected into `MixinXY`o.y=0-- so as `y`o.set_position=function(self, x, y)
self.x=xself.y=yendreturno-- actually dont need this lineendfunctionPlayer.new()
-- we let `Player` class extend the `MixinXY` interface---@classPlayer:MixinXYlocalself= {}
self.happy=truemixin_xy(self) -- no need `self =` here, otherwise will pollute `self` to become `Player|MixinXY` typereturnsetmetatable(self, Player)
endlocalp=Player.new() -- now this should also work
but...
As you can see there is a strange line local o = o inside function mixin_xy 😇
which is required because @class needs to be put above a variable definition line.
I don't know if there are better/simpler ways...
Hmmm it seems like it creates a lot of overhead for the developer to add ---@classes everywhere, mixin documentation and the actual mixin code itself in lua...
Is there a ---@Class that is less strict and allows fields to be freely injected? At this point the developer will need to remember both the documentation version of the Mixin and the actual mixin code.
(I am not maintainer but just a user/contributor, so the following are just based on my usage experience and knowledge)
Is there a ---@Class that is less strict and allows fields to be freely injected?
I don't think so and I agree that this maybe one of the limits of current LuaLS 😇
When you don't write any annotation, those types are anonymous
But since they just track all injected fields, it cannot be used for type checking
(i.e. checking if you are accessing undefined fields / injecting unwanted fields)
=> because there is no actual class definition for it
If you want type checking, then server must have a way to know which injected fields are allowed / disallowed
=> this is the usage of @class annotation
When using LuaLS you cannot dynamically create a class type, you have to use @class and define all its inheritance/mixins types.
Here is how I do mixins in my projects
a wrapper function that copy mixins interface to base class
---@classutil.copylocalcopy= {}
-- shallow copyfunctioncopy.shallow(A, B, doNotReplace)
fork, vinpairs(B) doifA[k] ==nilornotdoNotReplacethenA[k] =vendendreturnAend-- extend, shallow copy with mulitple args and will not replace if existfunctioncopy.extend(A, ...)
for_, Binipairs({...}) docopy.shallow(A, B, true)
endreturnAendreturncopy
then write the class and mixins in separate namespace
-- MixinXY.lua---@classMixinXYlocalMixinXY= {}
functionMixinXY:initPosition()
self.x=0self.y=0endfunctionMixinXY:setPosition(x, y)
self.x=xself.y=yend-- Player.lua---@classPlayer:MixinXY # write the inheritance herelocalPlayer= {}
copy.extend(Player, MixinXY) -- which match with the `extend` statementPlayer.__index=PlayerfunctionPlayer.new()
---@classPlayer # here just mark `self` as class, no need write inheritance againlocalself=setmetatable({}, Player) -- setmetatable at the beginningself.happy=trueself:initPosition() -- such that we can call methods right awayreturnselfendlocalp=Player.new() -- now this should also work
for inheriting a parent class and with multiple mixins
Note that @class can be written multiple times.
So if your Player class have another parent class, you may write like this:
Activity
tomlau10 commentedon Jan 9, 2026
improper use of
@classWhen you use
@class, only the fields directly injected into a variable marked by@class, or fields that are defined in method of this variable will be considered as fields of that class.In you example
how to use
@classin your exampleIf you want to use
@class, one way is like thisif your want
mixin_xyfor more general useyou will have to define it as another class (interface)
but...
As you can see there is a strange line
local o = oinsidefunction mixin_xy😇which is required because
@classneeds to be put above a variable definition line.I don't know if there are better/simpler ways...
distantforest1 commentedon Jan 9, 2026
Hmmm it seems like it creates a lot of overhead for the developer to add
---@classeseverywhere, mixin documentation and the actual mixin code itself in lua...Is there a ---@Class that is less strict and allows fields to be freely injected? At this point the developer will need to remember both the documentation version of the Mixin and the actual mixin code.
tomlau10 commentedon Jan 10, 2026
(I am not maintainer but just a user/contributor, so the following are just based on my usage experience and knowledge)
I don't think so and I agree that this maybe one of the limits of current LuaLS 😇
(i.e. checking if you are accessing undefined fields / injecting unwanted fields)
=> because there is no actual class definition for it
=> this is the usage of
@classannotationWhen using LuaLS you cannot dynamically create a class type, you have to use
@classand define all its inheritance/mixins types.Here is how I do mixins in my projects
for inheriting a parent class and with multiple mixins
Note that
@classcan be written multiple times.So if your
Playerclass have another parent class, you may write like this:=>
PcinheritsCharacterclass, and then mixin with a number of interfaces