@tabooharmony/roblox-luau-types
Use for Luau annotations, generics, unions, narrowing, strictness, sealed tables, module type exports, or typed metatables.
| name | roblox-luau-types |
| description | Use for Luau annotations, generics, unions, narrowing, strictness, sealed tables, module type exports, or typed metatables. |
| last_reviewed | 2026-08-21 |
| sources | https://luau-lang.org/typecheck, https://raw.githubusercontent.com/Roblox/creator-docs/main/content/en-us/luau/type-checking.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
Loading...
Select a file to preview
Analyzing security...
Checking scan reports and verification data.
Bill of Materials
Everything this skill can do — files, network, commands, and more.