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.
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.
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.
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:
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!!!
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.
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
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’ve tried experimenting with many solutions, but I think we’d need something like Type Conditionals or Generic Overloading to successfully achieve this.
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.
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.