Source: Luau.org · MIT · View source Imported 2026-10-03. Formatting adapted for this site.
Union and Intersection Types
Union types
A union type represents one of the types in this set. If you try to pass a union onto another thing that expects a more specific type, it will fail.
For example, what if this string | number was passed into something that expects number, but the passed in value was actually a string?
local stringOrNumber: string | number = "foo"
local onlyString: string = stringOrNumber -- not ok
local onlyNumber: number = stringOrNumber -- not ok Note: it's impossible to call a value of a union type if there are two or more function types in this union.
Tagged unions
Tagged unions are just union types! In particular, they're union types of tables where they have at least some common properties but the structure of the tables are different enough. Here's one example:
type Ok<T> = { type: "ok", value: T }
type Err<E> = { type: "err", error: E }
type Result<T, E> = Ok<T> | Err<E> This Result<T, E> type can be discriminated by using type refinements on the property type, like so:
--!file main.luau
local res = require("./result")
local result: res.Result<number, string> = res.something()
if result.type == "ok" then
-- result is known to be Ok<T>
-- and attempting to index for error here will fail
print(result.value)
elseif result.type == "err" then
-- result is known to be Err<E>
-- and attempting to index for value here will fail
print(result.error)
end
--!file result.luau
export type Ok<T> = { type: "ok", value: T }
export type Err<E> = { type: "err", error: E }
export type Result<T, E> = Ok<T> | Err<E>
return {
something = function(): any return {} end
} Which works out because value: T exists only when type is in actual fact "ok", and error: E exists only when type is in actual fact "err".
Intersection types
An intersection type represents all of the types in this set. It's useful for two main things: to join multiple tables together, or to specify overloadable functions.
type XCoord = {x: number}
type YCoord = {y: number}
type ZCoord = {z: number}
type Vector2 = XCoord & YCoord
type Vector3 = XCoord & YCoord & ZCoord
local vec2: Vector2 = {x = 1, y = 2} -- ok
local vec3: Vector3 = {x = 1, y = 2, z = 3} -- ok type SimpleOverloadedFunction = ((string) -> number) & ((number) -> string)
local f: SimpleOverloadedFunction
local r1: number = f("foo") -- ok
local r2: number = f(12345) -- not ok
local r3: string = f("foo") -- not ok
local r4: string = f(12345) -- ok Note: it's impossible to create an intersection type of some primitive types, e.g. string & number, or string & boolean, or other variations thereof.
Note: Luau still does not support user-defined overloaded functions. Some Roblox and Lua 5.1 functions have different function signatures, so they inherently require overloaded functions.