Proposal to add dynamic function return types

As a Roblox developer, it is currently not possible to dynamically change the return type of a function from within the function itself. While generic typing can be used for cases where a function’s return type changes to match the type of an argument passed to that function, as shown in this barebone example:

local function generic<T>(value: T): T
	return value
end

print(generic(1234)) -- generic(value: number): number
print(generic("Hello!")) -- generic(value: string): string

a method to change the return type of a function from within itself isn’t currently implemented, but would allow ideal compatibility with strict type-checking for cases such as the example shown below for a function that creates and sets the properties of an Instance using a dictionary:

local function create(className: string, parent: Instance?, properties: {[string]: any}?)
	local instance = Instance.new(className)

	if properties then
		for property, value in properties do
			if property == "Parent" then continue end

			instance[property] = value
		end
	end

	instance.Parent = parent

	return instance
end

local part: Part = create(
	"Part",
	workspace,
	{
		Color = Color3.new(1, 0.5, 0),
		Size = Vector3.one * 4,
		Anchored = true
	}
)

If this issue is addressed, it would improve my development experience because it would be much more convenient if a method such as the one shown in this example would be possible to use:

local function create(className: string, parent: Instance?, properties: {[string]: any}?): dynamic
	local instance = Instance.new(className)

	dynamic:: typeof(instance)

	if properties then
		for property, value in properties do
			if property == "Parent" then continue end

			instance[property] = value
		end
	end

	instance.Parent = parent

	return instance
end

local part = create(
	"Part",
	workspace,
	{
		Color = Color3.new(1, 0.5, 0),
		Size = Vector3.one * 4,
		Anchored = true
	}
)
3 Likes

This seems like a classic XY Problem. You want to have some way to get the Instance type associated with its ClassName, but you designed the feature request around the proposed solution!

If you have other use cases for dynamic types which cannot be done with the current checker’s tools, feel free to append them. Regardless, it’s unlikely this will be added due to the complexity of recursive sub-scoping.

I don’t see how this applies. Instance.new() already has a dynamic return type, I don’t see why such a functionality has to be exclusive to built-in methods.

2 Likes

Couldn’t you just return typeof(Instance.new(className)) or does that not work? Btw why does Instance.new use a string instead of a generic type to create an instance.
Also, a function like shown in the example couldn’t even work because return types must be determined at compile time.

No, since to use typeof for creating a return type for a function would require doing something like this:

type object

local function create(className: string, parent: Instance?, properties: {[string]: any}?): object
	local instance = Instance.new(className)

	object = typeof(instance)

	if properties then
		for property, value in properties do
			if property == "Parent" then continue end

			instance[property] = value
		end
	end

	instance.Parent = parent

	return instance
end

Which result in the errors: Expected type, got 'local' and Global 'object' is only used in the enclosing function 'create'; consider changing it to local since it’s currently not supported behavior to postpone setting a type or to redefine a type

The way generic typing works is the return type is equal to the input parameter’s type, so if it didn’t use strings as input we’d need to input a Part for the return type to be a Part, which isn’t practical for a function whose purpose is to create a varied type of Instances

Like @betrelIe said, Instance.new is already able to do it, so why not allow us to do it as well if need be? It has use cases like for example if your game has a mage class and you’re using OOP for each spell, currently if you’d like to use type-checking it requires the use of separate constructors for each spell, but if dynamic typing is available one constructor would be enough since it will give you the correct type for the created spell

Because it has a ‘magic-type’, just like string.format and select. They’re behavior is hard coded in C++ because the typechecker has no concept of conditional typing.


Again, you are asking for a proposed solution to a problem instead of explaining what you want to do, which isn’t helpful to engineers. If you want a way to get an instance type from its classname, just ask for that!

That would only be a partial solution toward the problem though, it won’t help for cases like this:

I did some digging trying to find out whatever the hell a “magic-type” is and I found that with a semi-hacky and arguably sluggish workaround you can indeed incorporate conditional typing akin to Instance.new()'s dynamic return type.

As OP said, your rendition of this problem isn’t sufficient and is pretty limited in its application. What we would’ve wanted was a way for us to write functions whose return types vary depending on the value of an argument, like how Instance.new() works.

I’ll concede that this feature request likely isn’t necessary for me now that I managed to find a workaround but I wish there was a cleaner, native implementation of it.

1 Like