Boba is a general-purpose runtime typechecking library for Luau.
- Zero
gt.buildor:: anywith proper Luau type-safety - Deduplicate code by building Luau types and runtime types together
- Significantly smaller LOCs compared to GreenTea, t, and ty
- Choose between standalone Luau and Roblox flavored Boba
- Great DX with helpful errors and rbxts typings included
Warning
Work is in progress for automating package managers. For now, copy in whatever packages you need into your codebase.
# luau
pesde install @wthrblx/boba
wally install @wthrblx/boba
# rbxts
npm install @rbxts/boba
pnpm install @rbxts/boba
bun install @rbxts/bobaDefine types with Boba's various constructors. These types can be tested against unknown objects:
local DamageAmount = Boba.Or(
Boba.Number,
Boba.Or(
Boba.Literal("Lethal" :: "Lethal"),
Boba.Nil
)
)Boba constructors are also exposed as methods on types, so you can easily chain them for greater clarity:
local DamageAmount = Boba.String
:Or(Boba.Literal("Lethal" :: "Lethal"))
:Or(Boba.Nil)To infer a static type, call typeof() on the type's inner property:
-- inferred as `number | "Lethal" | nil`!
-- (note that types for literals may be widened, this is a limitation with Luau)
export type DamageAmount = typeof(DamageAmount.inner)Optionally, finish types by giving it a nickname:
local DamageAmount = Boba.String
:Or(Boba.Literal("Lethal" :: "Lethal"))
:Or(Boba.Nil)
:Nickname("DamageAmount")Now, DamageAmount will be printed as DamageAmount and not
number | "Lethal" | nil.
At runtime, inner references itself. This enables building struct types
while resolving the correct types:
local Turret = Boba.ExhaustiveStruct {
cooldown = Boba.Number.inner,
amount = DamageAmount.inner,
}
-- inferred as { cooldown: number, amount: number | "Lethal" | nil }!
export type Turret = typeof(Turret.inner)Tip
For TypeScript users, you can omit .inner for structs:
const Turret = Boba.ExhaustiveStruct({
cooldown: Boba.Number,
amount: DamageAmount,
})
export type Turret = typeof Turret.inner
// alternatively
export type Turret = Boba.Infer<typeof Turret>
// still infers { cooldown: number, amount: number | "Lethal" | undefined }!Use check to see if an unknown value matches the type, receiving an error
message otherwise:
local unknownData = {
cooldown = 2,
amount = "Deadly"
}
local isTurret, turretError = Turret:check(unknownData)
print(isTurret, turretError)Boba will return precise, helpful errors:
expected { cooldown: number, amount: DamageAmount }
> value at key "amount" (string)
expected DamageAmount
> did not match any of: number | Lethal, nil
expected nil
> only nil is accepted, but got "Deadly" (string)
expected number | Lethal
> did not match any of: number, Lethal
expected Lethal
> only Lethal is accepted, but got "Deadly" (string)
expected number
> got "Deadly" (string)
If you're in a rush, you can assert values:
-- this will error if the input doesn't match the type.
local validatedData = Turret:assert(unknownData)
-- zero type errors!
print(validatedData.cooldown)Congrats! You've learned all of what Boba offers.
You can read the documentation (SOON) for all the other trinkets that Boba exposes.
# luau
pesde install @wthrblx/boba-roblox
# rbxts
npm install @rbxts/boba-roblox
pnpm install @rbxts/boba-roblox
bun install @rbxts/boba-robloxCompatibility is a work in progress, though the plan is to support both t and GreenTea.
