I am trying to make a fairly complex library for handling multiple asynchronous functions/threads, but have come to a very specific problem with typing it.
Essentially I am trying to have a function “all” that takes a variadic of functions and returns the first one to return something. I want it so the return is relative to the passed functions, so obviously I should use type packs, but I’m having trouble getting them to work.
local function all<T...>(...: (...any) -> T...): T...
-- spawns a thread for each function
-- and returns the first function to finish's return
end
-- dummy functions
local a: () -> number = nil
local b: () -> string = nil
local c: () -> boolean = nil
-- b & c flag, because they do not
-- share the same return type as a
local foo = all(a, b, c)
-- foo is typed 'number', when it should be 'number | string | boolean'
What should I do to “union” the packs so I get the desired type of number | string | boolean?
I would say just dont worry about it since type annotations dont affect the code, only what warnings the editor shows. If your function is well documented and doesn’t combine too much functionality unnecessarily it will be all ok.
Sorry if I misunderstood the question, but does this achieve what you wanted?
--!strict
type UnionType = number & string & boolean
local function all<T...>(...: (...any) -> T...): T...
-- spawns a thread for each function
-- and returns the first function to finish's return
end
-- dummy functions
local a: () -> UnionType = nil
local b: () -> UnionType = nil
local c: () -> UnionType = nil
-- b & c flag, because they do not
-- share the same return type as a
local foo = all(a, b, c)
-- foo is typed 'number', when it should be 'number | string | boolean'
Maybe I should rephrase what I am trying to achieve.
The function all takes in multiple functions with multiple return types (It could be anything, a specific table, a type of userdata, a tuple…) and its return type would be a union of all of those.
With type packs, I am able to get atleast one of the return types for the function, so
local function foo<T...>(func: () -> T...): T...
return func()
end
local function bar()
return 1, true, {nil}
end
-- return type is 'number, boolean and {nil}'
local a, b, c = foo(bar)
(Analysis already applies packs to the function without annotation, but this is just a representation of how that would type)
But now if I had it so two functions with two potentially different returns, there becomes a problem, namely I cannot just union two variadic type packs:
local function bar() --> number
return 1
end
local function baz() --> boolean
return true
end
-- A... | B... is not valid syntax
-- How would I union the type pack?
local function foo<A..., B...>(func1: () -> A..., func2: () -> B...): A... | B...
if math.random(1, 2) == 1 then
return func1()
else
return func2()
end
end
-- I want value to infer as 'number | boolean'
local value = foo(bar, baz)
In my case with all, there could hundreds of functions passed through, so the solution would also have to account for annotating variadics with completely different return types. Does that make sense?