Implement tagged tuples

Currently, there is no way for intellisense to assume a type for the rest of a tuple given the first tuple.

For example, here’s a function conditionally returning false, number if the code failed, or true, nil if the code succeeded.

local function wasSuccessful(): (boolean, number?)
    if math.random(0, 1) == 0 then
        return false, 401
    else
        return true
    end
end

Let’s say we want to perform logic on the returned values. We would have to do something like this:

local success, errorCode = wasSuccessful()

if success then
    print("success!")
else
    print(errorCode:: number + 1)
end

As you can see, we have to cast a type number to errorCode because errorCode has a type of number? even though we know that when success is false, errorCode will always be number.

What I’m proposing is to add tagged tuples, which function similar to tagged unions. You can specify different return results and the typechecker will automatically infer a variable’s type:

type variableReturn = (true, nil) | (true, number)

local function wasSuccessful(): variableReturn
    if math.random(0, 1) == 0 then
        return false, 401
    else
        return true
    end
end

Now, we can do something like this:

if success then
    print("success!")
else
   -->> errorCode is now infered as "number" here
    print(errorCode + 1) --> no need to typecast, yay!
end
3 Likes

agreed that this would heavily improve typechecking, currently the only workaround would be to intersect the function’s type but it just feels really clunky

local function _()
    error('uke9emc9wowk')
end::(() -> false) & (() -> (true, number))

this feels like it’d be a better fit for an rfc for luau itself though, see GitHub - luau-lang/rfcs: RFCs for Luau evolution

1 Like

The main problem here is that tuples aren’t treated as single types but groups of opaque types and switching that model could be breaking. For example, (1, 2, 3) :: number yields 1 because the typecast intends for there to be only one number.

You can do ‘tagged union tables’ for records, though. {A: false, B: nil} | {A: true, B: number} will refine nicely.

1 Like