Skip to content

wthrblx/boba

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Boba

Boba is a general-purpose runtime typechecking library for Luau.

  • Zero gt.build or :: any with 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

License

Zlib License.

Usage

Installation

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/boba

Types

Define 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 }!

Validation

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.

Roblox

Installation

# luau
pesde install @wthrblx/boba-roblox
# rbxts
npm install @rbxts/boba-roblox
pnpm install @rbxts/boba-roblox
bun install @rbxts/boba-roblox

Data Types

Instances

Compatibility

Compatibility is a work in progress, though the plan is to support both t and GreenTea.

About

🧋 Best-of-every-world Luau runtime typechecker

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages