lua-language-server
lua-language-server copied to clipboard
Unioned Function Types don't properly work as expected
How are you using the lua-language-server?
Visual Studio Code Extension (sumneko.lua)
Which OS are you using?
Linux
What is the issue affecting?
Annotations, Type Checking, Completion
Expected Behaviour
When given a type like
---@type (fun(dataModel: DataModel, service: '\'AdService\''): AdService)|(fun(dataModel: DataModel, service: '\'Players\''): Players)
DataModel.b = function(dataModel,service)--[[some logic]]end;
followed by
local obj = DataModel:b('AdService')
the type of obj
should always be AdService
- if 'AdService'
is changed to 'Players'
, it should be of type Players
, similar to strongly-typed languages like TypeScript and Luau.
Actual Behaviour
In the above example, obj
is always of type AdService | Players
.
Reproduction steps
See Above
Minimal Code Snippet:
--- @type (fun(type: '\'string\''): string) | (fun(type:'\'number\''):number)
local exampleFunc = function(dataType)return dataType == 'number' and 1 or 'hi';end;
local num = exampleFunc('number') --> desired type: number - received type: string | number
local str = exampleFunc('string') --> desired type: string - received type: string | number
Additional Notes
No response
Log File
No response
Bear in mind that in LuaCATS annotations (the ones used by this language server), the |
character means union of types, not boolean OR operator.
Bear in mind that in LuaCATS annotations (the ones used by this language server), the
|
character means union of types, not boolean OR operator.
On the other hand I'm not sure if the union of two functions is a function that can take any of their parameters and return any of their results. I haven't looked into the theory behind it, but in practice it seems to weaken the type system. For example:
---@type (fun(a : integer) : string) | (fun(a : string) : integer)
local f
local r_str = f(1)
local r_int = f('hi')
math.random(r_int) -- cannot assign string|integer to integer
A more exclusive definition of function union seems like a more useful construct, if the language server can support it.
From what little I know about TypeScript, it seems that LuaCATS |
character works more like an intersection (&
) operator rather than a union (especially coupled with the lack of support for exact/fixed types)
I tried my example in TypeScript and it actually works differently. If I use |
(union), the parameters are actually intersected and it's not possible to call the function with any arguments:
type f1 = (fruit: string) => number;
type f2 = (color: number) => string;
declare let f: f1 | f2;
let r_str = f(1) // argument of type number is not assignable to never
let r_int = f('hi') // argument of type string is not assignable to never
Using &
works as expected, however:
type f1 = (fruit: string) => number;
type f2 = (color: number) => string;
declare let f: f1 & f2;
let r_str = f(1) // r_str is string
let r_int = f('hi') // r_int is number
I also found this release notes page talking about the behavior of function unions.
In any case, TypeScript's behavior with both | and & is more consistent and useful than LuaCATS in my opinion. A union of functions should either restrict the parameters to their common intersection, or treat each function as a separate type (what this issue asks for).
Bear in mind that in LuaCATS annotations (the ones used by this language server), the
|
character means union of types, not boolean OR operator.On the other hand I'm not sure if the union of two functions is a function that can take any of their parameters and return any of their results. I haven't looked into the theory behind it, but in practice it seems to weaken the type system. For example:
---@type (fun(a : integer) : string) | (fun(a : string) : integer) local f local r_str = f(1) local r_int = f('hi') math.random(r_int) -- cannot assign string|integer to integer
A more exclusive definition of function union seems like a more useful construct, if the language server can support it.
From what little I know about TypeScript, it seems that LuaCATS
|
character works more like an intersection (&
) operator rather than a union (especially coupled with the lack of support for exact/fixed types)
I believe that there is a prerequisite before implementing what you are describing. The language server can't determine the correct overload based on argument types. See #2084 for a brief overview of the issue, and #1456 covers it in detail.
I don't know if this is the correct place, but the issue https://github.com/LuaLS/lua-language-server/issues/2214 (which has been closed), only started surfacing after version 3.6.24; https://github.com/LuaLS/lua-language-server/compare/3.6.23...3.6.24
Version 3.6.23 doesn't output these warnings
This appears to have been fixed.