$npx -y skills add TabooHarmony/roblox-brain --skill roblox-luau-typesUse for Luau annotations, generics, unions, narrowing, strictness, sealed tables, module type exports, or typed metatables.
| 1 | # Luau Type System |
| 2 | |
| 3 | ## When to Load |
| 4 | |
| 5 | 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`. |
| 6 | |
| 7 | ## Quick Reference |
| 8 | |
| 9 | **Strictness:** `--!strict` (new code), `--!nonstrict` (transitional), `--!nocheck` (legacy only). The New Type Solver (GA Nov 2025) is faster/more accurate. |
| 10 | |
| 11 | **Inference philosophy:** Infer first, annotate boundaries (params, returns, exports). Don't annotate every local — noise hides signal. |
| 12 | |
| 13 | **Sealed vs unsealed tables:** |
| 14 | ```luau |
| 15 | local t = {} -- unsealed: can add fields |
| 16 | t.x = 1 -- OK |
| 17 | |
| 18 | local t: {x: number} = {x=1} -- sealed: no new fields |
| 19 | t.y = 2 -- ERROR |
| 20 | ``` |
| 21 | Build tables fully before annotating. Passing/returning seals them. |
| 22 | |
| 23 | **Unions & tagged unions:** |
| 24 | ```luau |
| 25 | local id: string | number = "abc" |
| 26 | type State<T> = {kind:"loading"} | {kind:"ready", value:T} | {kind:"fail", msg:string} |
| 27 | -- Discriminate: if state.kind == "ready" then state.value is narrowed |
| 28 | ``` |
| 29 | |
| 30 | **Narrowing:** |
| 31 | ```luau |
| 32 | if typeof(x) == "string" then ... end -- primitive narrowing |
| 33 | if inst:IsA("BasePart") then ... end -- Instance narrowing |
| 34 | assert(val, "missing") -- non-nil narrowing |
| 35 | ``` |
| 36 | |
| 37 | **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`. |
| 38 | |
| 39 | **Type exports:** `export type Foo = {...}` at module boundary. Consumers use `require` + `Types.Foo`. |
| 40 | |
| 41 | **Object typing:** `export type Counter = typeof(setmetatable({} :: CounterData, Counter))` for precise self. |
| 42 | |
| 43 | **Casts (::):** Precision tool to narrow overly generic inference — never to hide errors. |
| 44 | |
| 45 | **Key mistakes:** Unsealed `any` propagation in nonstrict, sealing tables too early, unions without discriminants, annotating every local. |
| 46 | |
| 47 | > Full reference: see `references/full.md` |