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()
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.