Literal types for enum-like behavior -- verifying variable arguments at runtime is tedious

As a Roblox developer it is annoying to have to verify enum-like arguments!

Say I have a function that takes a state as an argument, for e.g toggling an animation, the signature of such function may be

toggle_animation(state: string)

where state can be "stop", "start", "pause", or "resume". The first few lines of this function would need to check that the state given is valid, which is a waste of time that could be spent implementing the toggling behavior.

Another use case is for config values, for example in a tile-based game, you may only want certain tile sizes (e.g multiples of 8). Once more, a bunch of runtime type checking is needed.

So I propose literal types, based on typescript. The best part about this is that intellisense can show all the possible values of literal types, which is another use case. If you are using 3rd-party libraries or just have quite a large codebase (probably still using 3rd-party libraries lol), it can be easy to forget valid parameters.

type direction = "up" | "down" | "left" | "right"

local move_item: (direction) -> ()

move_item("up") -- good
move_item("down") -- good
move_item("UP") -- bad; linter should take effect
move_item("forward") -- bad; linter should take effect

If this feature was added, it would improve the programming experience as programmers can know what values are expected, and make catching typos easier (since at compile-time).

14 Likes

Would this work for numbers and booleans?

Will type deduction change to work with these types?

--!strict
local s = "string" -- will s be deduced to be "string" or string?
local q:"string" = s -- if s is deduced to be string then this warns
s = "different" -- if s is deduced to be "string" then this warns

This proposal seems similar to:

I don’t see why not, or is there a case where this would be ambiguous or otherwise complicated to resolve?

Luau currently doesn’t have const variables, so this should be string, not "string" unless the type is explicitly annotated. From what I understand, an example like const x = "abc" in typescript, the type of x would be "abc" as the variable is constant. But if it were a let (not annotated with type "abc"), this would be string. So if Luau ever does introduce const variables, this should be the behavior.

Thanks for the RFC – I will take a look at it.

Booleans are trivial, but supporting negative numbers is hard as there is no way to currently write a negative number as a numeric constant.

local x = -1 -- this is really a - followed by 1
local q:-1 = -1 -- so to support this it needs to at least work with a `-` operator

If unary minus is used to get negative numbers in a type it seems like other operators should be supported in a type, which would be complicated.