tabooharmony/roblox-brain

roblox-luau-types

Use for Luau annotations, generics, unions, narrowing, strictness, sealed tables, module type exports, or typed metatables.

Trending #9001 First seen May 28, 2026

Installation

$ npx skills add tabooharmony/roblox-brain --skill roblox-luau-types

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from tabooharmony/roblox-brain · top by installs.

npx skills add tabooharmony/roblox-brain

Browse all from tabooharmony/roblox-brain

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 42
License LICENSE
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 2,830 B
  • docs SUMMARY.md 145 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 588 installs

SKILL.md

Luau Type System

When to Load

Load for Luau type system work: annotations, generics, union types, type narrowing, sealed/unsealed tables, strictness modes (--!strict vs --!nonstrict), module type exports, and metatable-backed object typing. For syntax questions, use roblox-luau-core. For OOP/async/modules, use roblox-luau-patterns.

Quick Reference

Strictness: Use --!strict for maintained code, --!nonstrict while transitioning, and --!nocheck only for legacy/generated code. Project settings and directives select the mode; do not assume one global default.

Inference philosophy: Infer first, annotate boundaries (params, returns, exports). Don't annotate every local. Noise hides signal.

Sealed vs unsealed tables:

local t = {}          -- unsealed: can add fields
t.x = 1               -- OK

local t: {x: number} = {x=1}  -- sealed: no new fields
t.y = 2               -- ERROR

Build tables fully before annotating. Passing/returning seals them.

Unions & tagged unions:

local id: string | number = "abc"
type State<T> = {kind:"loading"} | {kind:"ready", value:T} | {kind:"fail", msg:string}
-- Discriminate: if state.kind == "ready" then state.value is narrowed

Narrowing:

if typeof(value) == "string" then
    print(string.upper(value)) -- primitive narrowing
end
if instance:IsA("BasePart") then
    print(instance.Position) -- Instance narrowing
end
assert(optionalValue, "missing") -- non-nil narrowing

Generics: Use when input→output type matters. function first<T>(list: {T}): T?. Generic aliases: type Result<T> = {success: boolean, value: T?}. Never replace with any.

Type exports: export type Foo = {...} at module boundary. Consumers use require + Types.Foo.

Object typing: export type Counter = typeof(setmetatable({} :: CounterData, Counter)) for precise self.

Casts (::): Precision tool to narrow overly generic inference, never to hide errors.

Write types you can trust: annotations are contracts for the compiler, not proof of runtime validity. Trust boundaries (remotes, DataStores, HttpService, attributes) still get runtime checks even when everything is annotated; inside a trusted boundary, let types carry the load instead of re-checking every call.

Key mistakes: Unsealed any propagation in nonstrict, sealing tables too early, unions without discriminants, annotating every local.

Full reference: see references/full.md