Note: this RFC was adapted from an internal proposal that predates RFC process
Status: Implemented
Implement syntax for type ascriptions using ::
Luau would like to provide a mechanism for requiring a value to be of a specific type:
-- Asserts that the result of a + b is a number.
-- Emits a type error if it isn't.
local foo = (a + b) as number
This syntax was proposed in the original Luau syntax proposal. Unfortunately, we discovered that there is a syntactical ambiguity with as
:
-- Two function calls or a type assertion?
foo() as (bar)
To provide this functionality without introducing syntactical confusion, we want to change this syntax to use the ::
symbol instead of as
:
local foo = (a + b) :: number
This syntax is borrowed from Haskell, where it performs the same function.
The ::
operator will bind very tightly, like as
:
-- type assertion applies to c, not (b + c).
local a = b + c :: number
Note that ::
can only cast a single value to a type - not a type pack (multiple values). This means that in the following context, ::
changes runtime behavior:
foo(1, bar()) -- passes all values returned by bar() to foo()
foo(1, bar() :: any) -- passes just the first value returned by bar() to foo()
It’s somewhat unusual for Lua to use symbols as operators, with the exception of arithmetics (and ..
). Also a lot of Luau users may be familiar with TypeScript, where the equivalent concept uses as
.
::
may make it more difficult for us to use Turbofish (::<>
) in the future.
We considered requiring as
to be wrapped in parentheses, and then relaxing this restriction where there’s no chance of syntactical ambiguity:
local foo: SomeType = (fn() as SomeType)
-- Parentheses not needed: unambiguous!
bar(foo as number)
We decided to not go with this due to concerns about the complexity of the grammar - it requires users to internalize knowledge of our parser to know when they need to surround an as
expression with parentheses. The rules for when you can leave the parentheses out are somewhat nonintuitive.