Loading...
Loading...
Modern, type-safe Luau for Roblox, covering --!strict and the new type solver, generics, string interpolation, if-expressions, task library, require-by-string, module/OOP patterns, error handling with retries, buffer/vector, and legacy APIs to avoid. Use when writing, refactoring, or explaining any Roblox script, fixing type errors, or modernizing old Lua code.
npx skill4agent add el4cteo/roblox-skills roblox-luau--!strictgame:GetService("Name")game.Nametasktask.waittask.spawntask.defertask.delaytask.cancelwaitspawndelay*Asyncpcall*AsyncLoadCharacterAsyncGetProductInfoAsyncUserHasBadgeAsyncPascalCasecamelCaseSCREAMING_SNAKE_CASElocal Players = game:GetService("Players")
local count = 0
count += 1 -- compound assignment: += -= *= /= //= %= ^= ..=
local label = if count > 1 then "many" else "one" -- if-expression (no `and/or` pitfalls)
print(`Player count: {#Players:GetPlayers()} ({label})`) -- string interpolation
for index, player in Players:GetPlayers() do -- generalized iteration: no pairs/ipairs needed
if player.AccountAge < 1 then
continue -- continue is a keyword
end
print(index, player.Name)
end
local half = 7 // 2 -- floor division → 3
local frozen = table.freeze({ Speed = 16 }) -- read-only table; writes throw
local list = table.create(100, 0) -- preallocated array
local copy = table.clone(frozen) -- shallow copy (unfrozen)
local mid = math.lerp(0, 10, 0.5) -- also: math.map, math.clamp, math.round, math.sign
print(half, list[1], copy.Speed, mid)pairsipairs#tt[i] = nilmath.floor//tick()os.clock()workspace:GetServerTimeNow()os.time()DateTime.now()Workspace.UseNewLuauTypeSolverWorkspace.LuauTypeCheckMode--!strict--!strict
export type Item = {
id: string,
name: string,
stack: number,
tags: { string }?, -- optional field
}
type Rarity = "Common" | "Rare" | "Legendary" -- singleton (literal) union
local function totalStack(items: { Item }, filter: ((Item) -> boolean)?): number
local total = 0
for _, item in items do
if filter == nil or filter(item) then
total += item.stack
end
end
return total
end
local function first<T>(list: { T }): T? -- generics
return list[1]
end
local rarity: Rarity = "Rare"
local part = workspace:FindFirstChild("Door") :: BasePart? -- cast with ::
print(totalStack({}, nil), first({ 1, 2 }), rarity, part)if part then ... endif typeof(x) == "Instance" and x:IsA("BasePart") thentypeof(v)"Vector3""Instance"type(v)IsAFindFirstChildOfClassFindFirstChildWhichIsAanyreadwriterequireReplicatedStoragelocal ReplicatedStorage = game:GetService("ReplicatedStorage")
local Signal = require(ReplicatedStorage.Packages.Signal) -- instance path (always works)
local Config = require("./Config") -- require-by-string: sibling of this script (script.Parent)
local Utils = require("@self/Utils") -- child of this script
local Shared = require("@game/ReplicatedStorage/Shared") -- absolute from game
print(Signal, Config, Utils, Shared)./script.Parent../script.Parent.Parent@self/script@game/gameWaitForChildWaitForChild--!strict
local Counter = {}
Counter.__index = Counter
export type Counter = typeof(setmetatable({} :: { value: number, step: number }, Counter))
function Counter.new(step: number?): Counter
return setmetatable({ value = 0, step = step or 1 }, Counter)
end
function Counter.increment(self: Counter): number
self.value += self.step
return self.value
end
return Counterlocal DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("PlayerData")
local function retry<T...>(attempts: number, fn: () -> T...): (boolean, T...)
local delaySeconds = 1
for attempt = 1, attempts do
local result = table.pack(pcall(fn))
if result[1] then
return table.unpack(result, 1, result.n)
end
if attempt < attempts then
warn(`attempt {attempt} failed: {result[2]}`)
task.wait(delaySeconds)
delaySeconds *= 2 -- exponential backoff
end
end
return false
end
local ok, data = retry(3, function()
return store:GetAsync("Player_1")
end)
print(ok, data)error("message", 2)error({ code = "X" })xpcall(fn, debug.traceback)pcall| Call | Behavior |
|---|---|
| Run now (until first yield) on a new thread. |
| Run at the end of the current resumption cycle. Good for "after this finishes". |
| Run after |
| Yield ≥ |
| Stop a spawned/delayed thread. |
| Parallel Luau (inside Actors) — see |
RunService.HeartbeatRunService.PreSimulationPostSimulationRunService:BindToRenderStepwhile true do task.wait() enddeltaTimeWorkspace.SignalBehaviorDeferredDefault:Fire()Changedlocal Players = game:GetService("Players")
local connections: { RBXScriptConnection } = {}
table.insert(
connections,
Players.PlayerRemoving:Connect(function(player)
print(`{player.Name} left`)
end)
)
local function cleanup()
for _, connection in connections do
connection:Disconnect()
end
table.clear(connections)
end
cleanup()Instance:Destroy()ParentPlayerRemoving:Once()local packet = buffer.create(10)
buffer.writeu16(packet, 0, 513) -- item id
buffer.writef32(packet, 2, 12.5) -- amount
buffer.writei32(packet, 6, -4)
print(buffer.readu16(packet, 0), buffer.readf32(packet, 2), buffer.len(packet))
local v = vector.create(1, 2, 3) -- native 3-float vector (same type as Vector3 at runtime)
print(vector.magnitude(v), vector.normalize(v), vector.dot(v, vector.one))buffer| Don't write | Write instead |
|---|---|
| |
| |
| create, set properties, then set |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
roblox-code-reviewdebug.profilebegin/profileend--!native@nativeInstanceFindFirstChildtable.concatbuffer..RaycastParamsOverlapParamsroblox-architectureroblox-networkingroblox-performanceroblox-code-review