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
thanks for putting all these in one place. i found some more edge cases; maybe some of them are listed here already but i'll try to document them more diligently.
^^^ here p1 should have a type of A within the function, but it doesn't, so m receives the type unknown
This is odd since this use case works but you have to use the parameter capture for the generic to get its type and it also only works with base Lua types and not custom classes
see #2355 description
^^^ here p1 should have a type of A within the function, but it doesn't, so m receives the type unknown
This is odd since this use case works but you have to use the parameter capture for the generic to get its type and it also only works with base Lua types and not custom classes see #2355 description
I wouldn't really consider that to be "working". Passing strings at runtime just to get the LSP to work correctly is awkward. In theory this should be an easy fix, as the concrete type is literally right there and apart of the function's signature. It's not much different than getting/substituting the type of a concrete @param, there's just an extra layer of indirection (i.e. p1 -> T -> A instead of p1 -> T).
^^^ here p1 should have a type of A within the function, but it doesn't, so m receives the type unknown
This is odd since this use case works but you have to use the parameter capture for the generic to get its type and it also only works with base Lua types and not custom classes see #2355 description
I wouldn't really consider that to be "working". Passing strings at runtime just to get the LSP to work correctly is awkward. In theory this should be an easy fix, as the concrete type is literally right there and apart of the function's signature. It's not much different than getting/substituting the type of a concrete @param, there's just an extra layer of indirection (i.e. p1 -> T -> A instead of p1 -> T).
Oh yeah, definitely not "working" per se but very odd that it works with strings and only base Lua types. I would think it would literally be easier to implement the other way code wise
Is it planned to have vararg generics?
The current type definitions for assert(v, message, ...) can only return the type of the first argument, but not of the varargs that are returned.
It would also be nice to get something like this working:
--- Calls function `f` with the given arguments in protected mode with a new message handler.------ @genericTa--- @genericTr------ @paramfasyncfun(...: Ta): ...: Tr The function to call.--- @parammsghfun(error: string) The error handler.--- @param ... TaArguments to call the function with.--- @returnTr ...functionxpcall(f, msgh, ...) end
(A few things are wrong, but this is the idea)
Another place where this could be useful is unpack(list, i, j) function, of which even the annotations do not work correctly at this time.
Reacted by ExE Boss, Tamika Nomara, shellRaining and Noah
Just wanted to note that this description from the docs needs a little more detail:
Surrounding the generic in backticks (`) will capture the value and use it for the type.
What does capture mean? What's the difference between T and `T`? It would help if the given example had a use with and without the backticks and explained what the difference is.
What does capture mean? What's the difference between T and `T`? It would help if the given example had a use with and without the backticks and explained what the difference is.
This came useful for me when I had a function receiving a generic parameter that I also wanted to be able to return. In other words the type capture means that you're telling the lsp to use that usage of the generics to determine its type (when referenced in other annotations as well):
---Resolve option inheritance etc. and set automatic enabler tables index fallbacks---@genericT:manipulator.ts.MethodOpts---@genericS:manipulator.ts.MethodOpts---@paramorig`S`---@paramopts?manipulator.ts.PresetOpts<`T`>---@returnS|Tlocalfunctionextend_opts(orig, opts)
returnopts.presetandM.presets[opts.preset] ororigend
@litoj I'm sorry but I still don't understand. It needs a side-by-side comparison. For instance, what is the difference between these two functions?
---@genericT---@paramargTlocalfoo=function(arg)
-- type hint is: function foo(arg: <T>)end---@genericT---@paramarg`T`localfoo2=function(arg)
-- type hint is: function foo(arg: <T>)end
The type hint for both of these is just function foo(arg: <T>). I've been trying various combinations of multiple args, multiple generics, different return types, etc. and I still don't understand the difference.
Maybe my example is not a good once since they are the same in this case (?) but could you provide a similar simple example that illustrates the difference?
So as far as I can tell, all the `s did was just remove some of the type information from the function signature. Without the `s, the function maintains the narrower type annotations for the args, but with the `s it just discards the parent type part. Is that the idea?
@nullromo I am not sure if @litoj 's example is a correct usage of backtick capture in LuaLS 😕
AFAIK, backtick capture basically means capture the literal string param value (at the caller side) as the type name.
Then you can use this type name in @return.
It's NOT about the type hint of your function, nor how you can use this T inside your function implementation.
example
---@genericT---@paramargT---@returnTlocalfoo1=function(arg) return {} endlocalr1=foo1("Test") --> r1: string-- because the type of "test" is string, so "T = string"---@genericT---@paramarg`T`---@returnTlocalfoo2=function(arg) return {} endlocalr2=foo2("Test") --> r2: Test-- because the the string value "Test" is converted to type name, so "T = Test`-- of course this requires you to have a `@class Test` first, otherwise no further completions for this `r2: Test`
more advanced usage
There is another more advanced usage, where you can combine this captured type name with other string
=> to form another combined type name
---@genericT---@paramargObject.`T`---@returnTlocalfoo3=function(arg) return {} endlocalr3=foo3("Test") --> r3: Object.Test-- the "Test" string is captured, and then combined with `Object.` to make `T = Object.Test`-- then finally return this `Object.Test` to caller
There is also a related PR #3149 which enhances this capture feature, to support capturing the actually param type as type name.
But seems maintainer don't agree with this new behavior and the PR is not merged for a long time 🙈
@tomlau10 Thank you for the explanation. This makes it very clear what the backticks are doing. I think part of the difficulty I had in understanding it is that I didn't see an immediate practical use of this feature. Why would I write a function where I have to tell it what type to return using a parameter? All I can think of is that Lua doesn't have true generics, so it would be something like this
/* typescript code */constfoo=<T>(arg1,arg2): T=>{// T is not actually accessible at all because it's a real generic}constr=foo<number>(x,y);
In Lua, we can't give <T> to the function, so we just pass it as an argument and don't actually use it.
---@genericT---@param__returnType`T`---@returnTlocalfoo=function(__returnType, arg1, arg2)
-- do not actually need to use __returnType here, just use arg1 and arg2endlocalr=foo('number', x, y)
This is my guess as to why this feature exists. Is this accurate?
more advanced usage
For this one, I see what it's doing but the syntax is a little confusing. Based on ---@generic T and ---@return T, I would expect the return type to still be T. I would expect something like ---@return Object.`T` to be required to get Object.Test as the reutrn type. But whatever, it makes sense enough as-is. And again, thank you for explaining!
I will look and see if I can make a PR that updates the wiki slightly to make the example a bit more clear while still being concise.
---@genericT:string[]---@paramp1`T`[]---@return{ [T]: string }functionfoo(p1) endlocalresult=foo({ "bar", "quux" })
print(foo.bar)
-- ^ type is stringprint(foo.quux)
-- ^ type is string
If I have a generic function returning a table, and I pass it a table with some instance fields (see kingdom example further down) then autocomplete will find the fields of the kingdom table just fine
so something like root(kingdom). will output gold, prestige, glory, etc correctly
so generic T will basically treat my table and its fields as a full type - good
---@generic T
---@param t T
---@return T
function root(t) return t end
but If I return generic function which accepts a table of the type T (see example of usage with another_kingdom) - it will not autocomplete nor find the gold/glory/prestige of the table, so it doesn't understand that this table is of type T the same way as simple return does
---@generic T
---@param t T
---@return fun(t:T): T
function instance_of(t)
return getmetatable(t).__call;
end
kingdom = entity {
gold = 10,
prestige = 10,
glory = 100
}
another_kingdom = instance_of(kingdom) {
>> if I try to autocomplete here plugin will not suggest gold, glory, prestige, it treats the table as opaque <<
gold = 100,
glory = 50,
prestige = 1
}
however if kingdom was of some --@type then it would suggest this type fields
but not the instance fields, which work with simple returns
would you be so kind as to add codepath similar to simple return for such cases?
it would greatly improve experience with my entity definition tables
Activity
bandaloo commentedon Jan 31, 2023
thanks for putting all these in one place. i found some more edge cases; maybe some of them are listed here already but i'll try to document them more diligently.
jakitliang commentedon Dec 2, 2023
When will it get done?
tmillr commentedon Jan 29, 2024
Here's another issue with generics that I don't believe has been documented yet?
^^^ here
p1should have a type ofAwithin the function, but it doesn't, thereforemreceives the typeunknowncheckraisefold commentedon Jun 27, 2024
This is odd since this use case works but you have to use the parameter capture for the generic to get its type and it also only works with base Lua types and not custom classes
see #2355 description
tmillr commentedon Jul 10, 2024
I wouldn't really consider that to be "working". Passing strings at runtime just to get the LSP to work correctly is awkward. In theory this should be an easy fix, as the concrete type is literally right there and apart of the function's signature. It's not much different than getting/substituting the type of a concrete
@param, there's just an extra layer of indirection (i.e.p1->T->Ainstead ofp1->T).checkraisefold commentedon Jul 10, 2024
Oh yeah, definitely not "working" per se but very odd that it works with strings and only base Lua types. I would think it would literally be easier to implement the other way code wise
AserJoker commentedon Sep 18, 2024
how to implement like
or not support now
CelDaemon commentedon Dec 29, 2024
Is it planned to have vararg generics?
The current type definitions for
assert(v, message, ...)can only return the type of the first argument, but not of the varargs that are returned.CelDaemon commentedon Jan 1, 2025
It would also be nice to get something like this working:
(A few things are wrong, but this is the idea)
Another place where this could be useful is
unpack(list, i, j)function, of which even the annotations do not work correctly at this time.brunoti commentedon Feb 10, 2025
anyone has made this work?
aiya000 commentedon Oct 11, 2025
Are there any issues regarding scoped generics?
I'm interested in this
:)This is a major feature in TypeScript and Haskell (GHC extensions).
This is a useful example of scoped generics, and there are many other useful applications.
litoj commentedon Nov 9, 2025
I noticed a surprising behaviour regarding generic type reuse/extraction from a generic class to a generic function:
This code shows warnings for accessing a non-existent field (
.test). When I requested the type info ofpresetI got:I have no idea what it is representing inside the lsp, but I do know that there is an extra pair of surrounding
<>nullromo commentedon Nov 10, 2025
Just wanted to note that this description from the docs needs a little more detail:
What does capture mean? What's the difference between
Tand`T`? It would help if the given example had a use with and without the backticks and explained what the difference is.litoj commentedon Nov 11, 2025
This came useful for me when I had a function receiving a generic parameter that I also wanted to be able to return. In other words the type capture means that you're telling the lsp to use that usage of the generics to determine its type (when referenced in other annotations as well):
nullromo commentedon Nov 11, 2025
@litoj I'm sorry but I still don't understand. It needs a side-by-side comparison. For instance, what is the difference between these two functions?
The type hint for both of these is just
function foo(arg: <T>). I've been trying various combinations of multiple args, multiple generics, different return types, etc. and I still don't understand the difference.Maybe my example is not a good once since they are the same in this case (?) but could you provide a similar simple example that illustrates the difference?
In your example, the function type hint is
But if I remove the
`s from your example, the type hint becomesSo as far as I can tell, all the
`s did was just remove some of the type information from the function signature. Without the`s, the function maintains the narrower type annotations for the args, but with the`s it just discards the parent type part. Is that the idea?tomlau10 commentedon Nov 12, 2025
@nullromo I am not sure if @litoj 's example is a correct usage of backtick capture in LuaLS 😕
AFAIK, backtick capture basically means capture the literal string param value (at the caller side) as the type name.
Then you can use this type name in
@return.It's NOT about the type hint of your function, nor how you can use this
Tinside your function implementation.example
more advanced usage
There is another more advanced usage, where you can combine this captured type name with other string
=> to form another combined type name
There is also a related PR #3149 which enhances this capture feature, to support capturing the actually param type as type name.
But seems maintainer don't agree with this new behavior and the PR is not merged for a long time 🙈
nullromo commentedon Nov 12, 2025
@tomlau10 Thank you for the explanation. This makes it very clear what the backticks are doing. I think part of the difficulty I had in understanding it is that I didn't see an immediate practical use of this feature. Why would I write a function where I have to tell it what type to return using a parameter? All I can think of is that Lua doesn't have true generics, so it would be something like this
In Lua, we can't give
<T>to the function, so we just pass it as an argument and don't actually use it.This is my guess as to why this feature exists. Is this accurate?
For this one, I see what it's doing but the syntax is a little confusing. Based on
---@generic Tand---@return T, I would expect the return type to still beT. I would expect something like---@return Object.`T`to be required to getObject.Testas the reutrn type. But whatever, it makes sense enough as-is. And again, thank you for explaining!I will look and see if I can make a PR that updates the wiki slightly to make the example a bit more clear while still being concise.
docs: explain backtick generic capture better
docs: explain backtick generic capture better (#78)
FunctionDJ commentedon Dec 11, 2025
Is something like this currently possible?
ian-pascoe commentedon Jan 12, 2026
I have a PR open to address several of these. Mainly the high-priority ones
chismar commentedon Feb 16, 2026
hi!
If I have a generic function returning a table, and I pass it a table with some instance fields (see kingdom example further down) then autocomplete will find the fields of the kingdom table just fine
so something like root(kingdom). will output gold, prestige, glory, etc correctly
so generic T will basically treat my table and its fields as a full type - good
but If I return generic function which accepts a table of the type T (see example of usage with another_kingdom) - it will not autocomplete nor find the gold/glory/prestige of the table, so it doesn't understand that this table is of type T the same way as simple return does
however if kingdom was of some --@type then it would suggest this type fields
but not the instance fields, which work with simple returns
would you be so kind as to add codepath similar to simple return for such cases?
it would greatly improve experience with my entity definition tables
Curve commentedon May 2, 2026
Is it possible to encode something like the following?