How to 'trick' Luau Typechecking into returning Instance classes based on function arguments?

Hello! As the title of this topic suggests, I’m trying to make Luau Typechecking recognize a Instance class based on a function argument.

For example:

-- Luau recognizes this as a Part ✅ and autocompletes the properties etc.
local Object = Instance.new("Part")
local function CreateInstance(ClassName: string)
	local Object = Instance.new(ClassName)
	return Object
end

-- Luau does not recognize this as a Part ❌ and does not autocomplete the properties etc.
local Object = CreateInstance("Part")

So how would I make Luau recognize the type/class that the function is creating based upon the ClassName parameter?

I have tried doing this:

local function CreateInstance(ClassName: string)
	-- Adding typeof(Instance.new(ClassName)) to set the type of the variable.
	local Object: typeof(Instance.new(ClassName)) = Instance.new(ClassName)
	return Object
end

-- Luau does not recognize this as a Part ❌ and does not autocomplete the properties etc.
local Object = CreateInstance("Part")

But no luck!

This is mildly infuriating when using --!strict because the typechecker keeps telling me it’s uncertain whether a value is a Part for example.

Anyone have an idea?

5 Likes

You can set the return type of a function by doing this:

function returnPart() : Part
    return Instance.new("Part");
end

--Luau should recognize this as a part, autocompleting as it should.
local part : Part = returnPart();

I’m unsure if this is the solution you’re looking for, as I don’t think typechecking would work with a variable type.

2 Likes

I see what you’re doing here, but that is unfortunately not the solution I’m looking for. I rather want it to work universally with any Instance class rather than just a Part.

2 Likes

just want to contribute here and say that this does NOT work, but it absolutely seems like it should:

local function CreateInstance(ClassName) : typeof(Instance.new(ClassName))
	
	local obj = Instance.new(ClassName)
	
	return obj
	
end

I did some extra testing and this does not work, as in Luau does not know the type of Instance.new(), which is probably why none of what has been tried works:

local ClassName = "Part"
Instance.new(ClassName)
5 Likes

Have you tried marking the return type as type Instance?

1 Like

It only recognizes the base instance properties

2 Likes

Sorry to necro an old post, but if anyone is curious, you could achieve this like this:

EDITT: I edited the below code to make it slightly cleaner. For the original, scroll down below.

function myCreator(className: string): Instance
    ...
end

local myCreator: typeof(Instance.new) = myCreator :: any
-- local assignment statements where the value
-- is the same variable as the one being assigned
-- are removed by the compiler now.

myCreator("Part").Col -- Typechecking!!!

It’s not perfect but it sort of works.

Original code
function myCreator_(className: string): Instance
  ...
end

local myCreator: typeof(Instance.new) = myCreator_ :: any

myCreator("Part") -- Typechecking!!!
2 Likes

Create a wrap. It’s simple as mapping out instances in a table and then return the values, you should use lua classes for this.

1 Like

Could you provide an example of this?

1 Like
local Part = Instance.new("Part")

local Wrap = {
 __Reference = Part;
 Position = Vector3.new();
 ...
}
local mt = {
 __index = function(tab,index)
     return tab.__Reference[index]
end
...
}
setmetatable(Wrap,mt)

This is the simplest examples and all of this could be raised in the complexity.

2 Likes

That’s cool but I’m confused as to how it would trick Luau’s type linting to return an Instance type based on the parameter of the function

1 Like

You can actually typeof the Instance.new function and override your function with it.

type __new = typeof(Instance.new)

local Create: __new = function(Name: string)
	return Instance.new(Name)
end :: any -- Unneeded, but using for Forward-Compatibility

Create("Part") -- Now it works!
-- Do note, we lose the argument names with this
7 Likes

This is really clever, I will try this later myself and see if it fits my situation. Thanks for getting back on an old post :slight_smile:

1 Like

This is smart, but in my situation I have a function that gets the children of a parent based on the ClassName that has been given. Kind of like this:

function GetChildren(Parent, ClassName) --// How do we tell Luau that we're going to return an array of "ClassName" instances?
    local childrenOfClass = {}
-- get children and run child.ClassName == ClassName then add to array
    return childrenOfClass
end

I don’t think there’s a way to do this though.

2 Likes

I’ve tried experimenting with many solutions, but I think we’d need something like Type Conditionals or Generic Overloading to successfully achieve this.

1 Like

Another thing you can do, but will take a long time to complete, is to use intersection types combined with function types:

type InstanceFactory =
	(("Part") -> Part)
& (("ObjectValue") -> ObjectValue)
& ...

function instanceCreator(className: string): Instance
	...
end

local instanceCreator: InstanceFactory = instanceCreator :: any

instanceCreator("Part"). -- Yay! Typechecking!

Of course, this would take a very VERY long time to complete so I would suggest automating it via a plugin that creates a ModuleScript which exports the type “InstanceCreator” and can be imported from other scritps.

8 Likes

wait bro, that is so smart you just solved 80% of my autocompletion problems.

2 Likes

That’s a smart thing to do, but it’s obviously not worth all the work. I just hope that Luau would be smart enough one day to do things like this.

1 Like

Yeah, that’s true. Although, as I said you could write a plugin to generate type modules for you depending on your input arguments. You could even have it generate other things such as instances in the explorer.

That said, I do agree that this should be a thing in Luau by default, in some form of capacity. But I have heard that they want to keep it very simple and not overcomplicate it like TS so maybe that isn’t so likely.

2 Likes

I feel like they should make it available in strict mode though, I know lots of people that would die for this update if it ever came.

2 Likes