Richer Autofill
Auto-fill your parameter names (a01: type begone!)
A signal implementation with a nice balance of ergonomics, performance, and features.

Named parameters, the key feature of NamedSignal, provides more semantic information than just the type alone.
Simply use the function type syntax to define names!
local catEvent = Signal.new<<
(name: string) -> ()
>>()
-- Parameter names and types get auto-filled!
catEvent:Connect(function(name: string)
print(`Hi {name}!`)
end)
catEvent:Fire("Herbert") --> "Hi Herbert!"Catch bugs before they happen with Luau's strict typechecking.
local stringAdded = Signal.new<<
(item: string) -> ()
>>()
-- TypeError: Expected this to be 'string', but got 'number'
stringAdded:Fire(100)
^^^Defer mutations for more predictable and consistent behavior, eliminating edge-cases.
local connection: Signal.Connection
Signal:Once(function()
connection:Disconnect()
end)
connection = Signal:Connect(function()
-- Some really important code that needs to run every cycle!
print("meow")
end)
Signal:Fire() --> "meow"Why?
Invocation is treated as sending a message to a snapshot of listeners; every listener that was connected at the time of :Fire() should get the message.
Have code that relies on immediate changes? There's an escape hatch.
Read more about deferred mutations on the dedicated page.
While NamedSignal was primarily designed with DX in mind, it's also optimized to perform well in the majority of use cases: from light signals to extremes with thousands of connections.
A minimized resumption dispatcher (also sometimes known as 'spinning threadloop'), results in a 600%+ speedup on signals with 10 connections, which along with other optimizations place NamedSignal on par with or ahead of other libraries.
Read more about optimizations and details on the dedicated page.
Check out the introduction to compare with other libraries, or get straight into installation.
All code and documentation was written entirely by hand.