How to make array return a union type

lets say my array is:
{“apple”, “banana”, “orange”}

i want to convert that into:
“apple” | “banana” | “orange”

how do i do that?

i tried this but only got the type ‘string’

code:

local array = {"apple", "banana", "orange"}

local function func(): index<typeof(array), keyof<typeof(array)>>
	
end

func()
2 Likes

well, you’d have to make a type for each fruit like this for example:

type apple = string

then you could use it as a custom type

soo your saying its not possible to do it with arrays?

i dont think so. are you trying to make a function that converts strings in an array to custom types?

i just want autocomplete when passing in an argument into a function

yeah i dont think its possible without having to declare the custom types first

im still new to type functions but i thought doing this would work but it just returns ‘nil’

local array = {"apple", "banana", "orange"}

type function result(ty: type)
	local union
	
	for property in ty:properties() do
		local value = property:value()
		
		if union then
			union = types.unionof(union, value)
		else
			union = value
		end
	end
	
	return union or types.singleton(nil)
end

local function func(): result<typeof(array)>
	
end

func()

do you think this new feature would fix it?
image
https://create.roblox.com/docs/release-notes/release-notes-713

You can force type with LuaU

local array: { type } = {}

For example,

local unions: { UnionOperation } = {union1, union2, ...}

I think UnionOperation is the type, not sure though. referencing this.


If you want the result of a function to be a type, you also do this for the result.

local function func(): { UnionOperation }

end

I’d provide an example if I had time, but like I’m not available easily rn so uh sorry.

not exactly. i want to make it return a union of specific strings without having to rewrite the table again

1 Like

Unfortunately I think the easiest way to do this right now is to manually cast all the strings back to themselves, AKA:

local array = { "apple" :: "apple", "banana" :: "banana", "orange" :: "orange" }
local fruit = array[1]

You can do index<typeof(array), number> to get the union type itself.

The most straightforward approach to this is to construct a type representing an element of the array beforehand:

type FruitElement = "apple" | "banana" | "orange"

local array: { FruitElement } = { "apple", "banana", "orange", "other" } --"other" causes a type error

function getElement(n: number): FruitElement
	if n == 0 then
		return "nothing" --type error
	else
		return array[n]
	end
end

local x: FruitElement = getElement(1)

Another approach is to convert the array into a dictionary and use keyof to create a union type of all keys:

local dictionary = {
	["apple"] = 0,
	["banana"] = 1,
	["orange"] = 2,
}

type FruitElement = keyof<typeof(dictionary)>

local x: FruitElement = "apple" --ok
local y: FruitElement = "other" --type error

function getElement(n: number): FruitElement
	--Without the cast on `dictionary`, you'll get type error "Cannot iterate over a table without indexer"
	for k, v in dictionary :: { [FruitElement]: number } do
		if n == v then
			return k
		end
	end
	error("No match")
end

local z: FruitElement = getElement(1)

It might be possible to leverage a custom type function to iterate over an array, returning a type that is the union of every literal value, however I don’t know enough about type functions to get something working. I’ll typically use the first approach when the number of elements is low and unlikely to change often, the second approach when I know more elements will be added over time, or often settle for a generic string with guard assert calls or early returns.

1 Like