Luau function wrapper type

Yo, I’m trying to write the type for a function wrapper wrap. wrap takes a function callback and should return a function with the exact same type signature as callback.

This works for most cases:

local function wrap<T..., U...>(
	callback: (T...) -> (U...)
): (T...) -> (U...)
	return function(...: T...): (U...)
		-- do some other stuff

		return callback(...)
	end
end


wrapped is typed as (number) -> (), as it should be.

But it gets kinda funky when callback uses generics:

Here, wrapped is inferred as ({ string }) -> (), and if you call wrapped with a { number } argument first and then call it again with a { string } argument, it would be inferred as ({ number} ) -> () instead for some reason. Ideally, wrap would somehow “preserve” the generics in callback.

I’ve also tried resorting to explicitly typing the wrapped callbacks, but that only half-works…

As you can see, Luau doesn’t like when I wrap the generic function, and yet it still types the wrapped generic function correctly.

How can I write a type for wrap which handles generics correctly?

Make a bug report by sending a message to @Bug-Support

I don’t have an answer for you, because the type system is so buggy, but whatever answer you do find will probably be a workaround.

Does the same issue occur with e.g.

local function wrap<T>(arg: T): nil
  print(arg)
end

Or the equivalent with T... instead? Just to find a minimal example.

I’m not sure if type packs (T... and U...) not handling generics like I want them to is a bug. I mostly just want to know how I can write the type for wrap such that it returns a callback with the exact same type as the callback passed to it, with or without generics, if it is even possible in Luau right now.

Luau doesn’t lint anything in the below code snippet, and everything is typed correctly:

local function doGenericThing<T>(arg: {T})
	print(table.concat(arg, ", "))
end

doGenericThing({ 1, 2, 3 })
doGenericThing({ 'a', 'b', 'c' })

local function typePackYay<T...>(...: T...)
	print(...)
end

typePackYay(1, 'a', true)
typePackYay({ 1 }, function() end)

image

You can use print to determine if something is a function or not.

I am well aware that you can print a function…

I’m asking if there is a way to write a type for a wrapper function so that the returned function has the same type as the passed in function. This way, whoever uses the wrapper can make more use of the Luau type checker in their code. The Luau type checker catches potential errors during the authoring of your code (not after like printing/asserting and argument would) and highlights it. This is nice because there’s less of a chance for players to encounter bugs after you’ve pushed out an update.

The solution I will have to use is to simply not type wrap and explicitly type any wrapped functions.

local function wrap(callback: any): any
	return function(...)
		print("hi")
		
		return callback(...)
	end
end

local function doGenericThingy<T>(arg: { T })
	return table.concat(arg, ", ")
end

local wrappedGenericThingy: typeof(doGenericThingy)
wrappedGenericThingy = wrap(doGenericThingy)