tl icon indicating copy to clipboard operation
tl copied to clipboard

Type guard doesn't work properly with unions

Open RobertBouillon opened this issue 10 months ago • 2 comments

Type guards with unions that include a record (table) will only check for the table. Other values included in the union will not be captured.

global record Foo
  bar : string
end

global function repro(x:Foo|string|nil) : integer
  local y = x
  if y is string | Foo then
    return 1
  elseif y is nil then
    return 2
  end
  return 3 -- string falls through here!
end

print(repro({} as Foo)) --1
print(repro("1"))       --3
print(repro(nil))       --2

Workaround

You can add a manual type guard in place of the union, which then gets inferred, but only if:

  1. it's in an elseif block and
  2. the if statement starts with an is expression
global function repro(x:Foo|string|nil) : integer
  local y = x
  if y is nil then
    local z : nil = y 
    return 2
  elseif type(y) == "table" or type(y) == "string" then
    local z : Foo | string = y
    return 1
  end
  return 3 --No fall through
end

RobertBouillon avatar Apr 09 '24 01:04 RobertBouillon

Thanks for the report!

This should also function as a workaround:

  if y is string or y is Foo then

hishamhm avatar Apr 09 '24 14:04 hishamhm

Thanks for the report!

This should also function as a workaround:

  if y is string or y is Foo then

I like that better, thanks!

Incredible library, by the way. Takes a lot of the sting out of large Lua scripts, especially coming from C#, Java, and TS.

RobertBouillon avatar Apr 09 '24 14:04 RobertBouillon