Skip to main content

Trove

A Trove is helpful for tracking any sort of object during runtime that needs to get cleaned up at some point.

Types

Trackable

type Trackable = Instance | RBXScriptConnection | ConnectionLike | PromiseLike | thread | ((...any) → ...any) | Destroyable | DestroyableLowercase | Disconnectable | DisconnectableLowercase

Represents all trackable objects by Trove.

ConnectionLike

interface ConnectionLike {
Connectedboolean
Disconnect(self) → ()
}

SignalLike

interface SignalLike {
Connect(
self,
callback(...any) → ...any
) → ConnectionLike
Once(
self,
callback(...any) → ...any
) → ConnectionLike
}

PromiseLike

interface PromiseLike {
getStatus(self) → string
finally(
self,
callback(...any) → ...any
) → PromiseLike
cancel(self) → ()
}

Constructable

type Constructable = {new(A...) → T} | (A...) → T

Destroyable

interface Destroyable {
disconnect(self) → ()
}

DestroyableLowercase

interface DestroyableLowercase {
disconnect(self) → ()
}

Disconnectable

interface Disconnectable {
disconnect(self) → ()
}

DisconnectableLowercase

interface DisconnectableLowercase {
disconnect(self) → ()
}

Functions

new

Trove.new() → Trove

Constructs a Trove object.

local trove = Trove.new()

Add

Trove:Add(
objectany,--

Object to track

cleanupMethodstring?--

Optional cleanup name override

) → objectany

Adds an object to the trove. Once the trove is cleaned or destroyed, the object will also be cleaned up.

The following types are accepted (e.g. typeof(object)):

Type Cleanup
Instance object:Destroy()
RBXScriptConnection object:Disconnect()
function object()
thread task.cancel(object)
table object:Destroy() or object:Disconnect() or object:destroy() or object:disconnect()
table with cleanupMethod object:<cleanupMethod>()

Returns the object added.

-- Add a part to the trove, then destroy the trove,
-- which will also destroy the part:
local part = Instance.new("Part")
trove:Add(part)
trove:Destroy()

-- Add a function to the trove:
trove:Add(function()
	print("Cleanup!")
end)
trove:Destroy()

-- Standard cleanup from table:
local tbl = {}
function tbl:Destroy()
	print("Cleanup")
end
trove:Add(tbl)

-- Custom cleanup from table:
local tbl = {}
function tbl:DoSomething()
	print("Do something on cleanup")
end
trove:Add(tbl, "DoSomething")

Clone

Trove:Clone() → Instance

Clones the given instance and adds it to the trove. Shorthand for trove:Add(instance:Clone()).

local clonedPart = trove:Clone(somePart)

Construct

Trove:Construct(
class{new(Args...) → T} | (Args...) → T,
...Args...
) → T

Constructs a new object from either the table or function given.

If a table is given, the table's new function will be called with the given arguments.

If a function is given, the function will be called with the given arguments.

The result from either of the two options will be added to the trove.

This is shorthand for trove:Add(SomeClass.new(...)) and trove:Add(SomeFunction(...)).

local Signal = require(somewhere.Signal)

-- All of these are identical:
local s = trove:Construct(Signal)
local s = trove:Construct(Signal.new)
local s = trove:Construct(function() return Signal.new() end)
local s = trove:Add(Signal.new())

-- Even Roblox instances can be created:
local part = trove:Construct(Instance, "Part")

Connect

Trove:Connect(
fn(...any) → ()
) → RBXScriptConnection

Connects the function to the signal, adds the connection to the trove, and then returns the connection.

This is shorthand for trove:Add(signal:Connect(fn)).

trove:Connect(workspace.ChildAdded, function(instance)
	print(instance.Name .. " added to workspace")
end)

BindToRenderStep

Trove:BindToRenderStep(
namestring,
prioritynumber,
fn(dtnumber) → ()
) → ()

Calls RunService:BindToRenderStep and registers a function in the trove that will call RunService:UnbindFromRenderStep on cleanup.

trove:BindToRenderStep("Test", Enum.RenderPriority.Last.Value, function(dt)
	-- Do something
end)

AddPromise

Trove:AddPromise(promisePromise) → Promise

Gives the promise to the trove, which will cancel the promise if the trove is cleaned up or if the promise is removed. The exact promise is returned, thus allowing chaining.

trove:AddPromise(doSomethingThatReturnsAPromise())
	:andThen(function()
		print("Done")
	end)
-- Will cancel the above promise (assuming it didn't resolve immediately)
trove:Clean()

local p = trove:AddPromise(doSomethingThatReturnsAPromise())
-- Will also cancel the promise
trove:Remove(p)
Promise v4 Only

This is only compatible with the roblox-lua-promise library, version 4.

Remove

Trove:Remove(objectany) → ()

Removes the object from the Trove and cleans it up.

local part = Instance.new("Part")
trove:Add(part)
trove:Remove(part)

Extend

Trove:Extend() → Trove

Creates and adds another trove to itself. This is just shorthand for trove:Construct(Trove). This is useful for contexts where the trove object is present, but the class itself isn't.

NOTE

This does not clone the trove. In other words, the objects in the trove are not given to the new constructed trove. This is simply to construct a new Trove and add it as an object to track.

local trove = Trove.new()
local subTrove = trove:Extend()

trove:Clean() -- Cleans up the subTrove too

Clean

Trove:Clean() → ()

Cleans up all objects in the trove. This is similar to calling Remove on each object within the trove. The ordering of the objects removed is not guaranteed.

trove:Clean()

WrapClean

Trove:WrapClean() → ()

Returns a function that wraps the trove's Clean() method. Calling the returned function will clean up the trove.

This is often useful in contexts where functions are the primary mode for cleaning up an environment, such as in many "observer" patterns.

local cleanup = trove:WrapClean()

-- Sometime later...
cleanup()
-- Common observer pattern example:
someObserver(function()
	local trove = Trove.new()
	-- Foo
	return trove:WrapClean()
end)

AttachToInstance

Trove:AttachToInstance(instanceInstance) → RBXScriptConnection

Attaches the trove to a Roblox instance. Once this instance is removed from the game (parent or ancestor's parent set to nil), the trove will automatically clean up.

This inverses the ownership of the Trove object, and should only be used when necessary. In other words, the attached instance dictates when the trove is cleaned up, rather than the trove dictating the cleanup of the instance.

CAUTION

Will throw an error if instance is not a descendant of the game hierarchy.

trove:AttachToInstance(somePart)
trove:Add(function()
	print("Cleaned")
end)

-- Destroying the part will cause the trove to clean up, thus "Cleaned" printed:
somePart:Destroy()

Destroy

Trove:Destroy() → ()

Alias for trove:Clean().

trove:Destroy()
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Constructs a Trove object.\n\n```lua\nlocal trove = Trove.new()\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Trove"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 180,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Add",
            "desc": "Adds an object to the trove. Once the trove is cleaned or\ndestroyed, the object will also be cleaned up.\n\nThe following types are accepted (e.g. `typeof(object)`):\n\n| Type | Cleanup |\n| ---- | ------- |\n| `Instance` | `object:Destroy()` |\n| `RBXScriptConnection` | `object:Disconnect()` |\n| `function` | `object()` |\n| `thread` | `task.cancel(object)` |\n| `table` | `object:Destroy()` _or_ `object:Disconnect()` _or_ `object:destroy()` _or_ `object:disconnect()` |\n| `table` with `cleanupMethod` | `object:<cleanupMethod>()` |\n\nReturns the object added.\n\n```lua\n-- Add a part to the trove, then destroy the trove,\n-- which will also destroy the part:\nlocal part = Instance.new(\"Part\")\ntrove:Add(part)\ntrove:Destroy()\n\n-- Add a function to the trove:\ntrove:Add(function()\n\tprint(\"Cleanup!\")\nend)\ntrove:Destroy()\n\n-- Standard cleanup from table:\nlocal tbl = {}\nfunction tbl:Destroy()\n\tprint(\"Cleanup\")\nend\ntrove:Add(tbl)\n\n-- Custom cleanup from table:\nlocal tbl = {}\nfunction tbl:DoSomething()\n\tprint(\"Do something on cleanup\")\nend\ntrove:Add(tbl, \"DoSomething\")\n```",
            "params": [
                {
                    "name": "object",
                    "desc": "Object to track",
                    "lua_type": "any"
                },
                {
                    "name": "cleanupMethod",
                    "desc": "Optional cleanup name override",
                    "lua_type": "string?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "object: any"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 239,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Clone",
            "desc": "Clones the given instance and adds it to the trove. Shorthand for\n`trove:Add(instance:Clone())`.\n\n```lua\nlocal clonedPart = trove:Clone(somePart)\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Instance"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 261,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Construct",
            "desc": "Constructs a new object from either the\ntable or function given.\n\nIf a table is given, the table's `new`\nfunction will be called with the given\narguments.\n\nIf a function is given, the function will\nbe called with the given arguments.\n\nThe result from either of the two options\nwill be added to the trove.\n\nThis is shorthand for `trove:Add(SomeClass.new(...))`\nand `trove:Add(SomeFunction(...))`.\n\n```lua\nlocal Signal = require(somewhere.Signal)\n\n-- All of these are identical:\nlocal s = trove:Construct(Signal)\nlocal s = trove:Construct(Signal.new)\nlocal s = trove:Construct(function() return Signal.new() end)\nlocal s = trove:Add(Signal.new())\n\n-- Even Roblox instances can be created:\nlocal part = trove:Construct(Instance, \"Part\")\n```",
            "params": [
                {
                    "name": "class",
                    "desc": "",
                    "lua_type": "{ new(Args...) -> T } | (Args...) -> T"
                },
                {
                    "name": "...",
                    "desc": "",
                    "lua_type": "Args..."
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "T"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 304,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Connect",
            "desc": "Connects the function to the signal, adds the connection\nto the trove, and then returns the connection.\n\nThis is shorthand for `trove:Add(signal:Connect(fn))`.\n\n```lua\ntrove:Connect(workspace.ChildAdded, function(instance)\n\tprint(instance.Name .. \" added to workspace\")\nend)\n```",
            "params": [
                {
                    "name": "signal",
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                },
                {
                    "name": "fn",
                    "desc": "",
                    "lua_type": "(...: any) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptConnection"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 337,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "BindToRenderStep",
            "desc": "Calls `RunService:BindToRenderStep` and registers a function in the\ntrove that will call `RunService:UnbindFromRenderStep` on cleanup.\n\n```lua\ntrove:BindToRenderStep(\"Test\", Enum.RenderPriority.Last.Value, function(dt)\n\t-- Do something\nend)\n```",
            "params": [
                {
                    "name": "name",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "priority",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "fn",
                    "desc": "",
                    "lua_type": "(dt: number) -> ()"
                }
            ],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 360,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "AddPromise",
            "desc": "Gives the promise to the trove, which will cancel the promise if the trove is cleaned up or if the promise\nis removed. The exact promise is returned, thus allowing chaining.\n\n```lua\ntrove:AddPromise(doSomethingThatReturnsAPromise())\n\t:andThen(function()\n\t\tprint(\"Done\")\n\tend)\n-- Will cancel the above promise (assuming it didn't resolve immediately)\ntrove:Clean()\n\nlocal p = trove:AddPromise(doSomethingThatReturnsAPromise())\n-- Will also cancel the promise\ntrove:Remove(p)\n```\n\n:::caution Promise v4 Only\nThis is only compatible with the [roblox-lua-promise](https://eryn.io/roblox-lua-promise/) library, version 4.\n:::",
            "params": [
                {
                    "name": "promise",
                    "desc": "",
                    "lua_type": "Promise"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 397,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Remove",
            "desc": "Removes the object from the Trove and cleans it up.\n\n```lua\nlocal part = Instance.new(\"Part\")\ntrove:Add(part)\ntrove:Remove(part)\n```",
            "params": [
                {
                    "name": "object",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 429,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Extend",
            "desc": "Creates and adds another trove to itself. This is just shorthand\nfor `trove:Construct(Trove)`. This is useful for contexts where\nthe trove object is present, but the class itself isn't.\n\n:::note\nThis does _not_ clone the trove. In other words, the objects in the\ntrove are not given to the new constructed trove. This is simply to\nconstruct a new Trove and add it as an object to track.\n:::\n\n```lua\nlocal trove = Trove.new()\nlocal subTrove = trove:Extend()\n\ntrove:Clean() -- Cleans up the subTrove too\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Trove"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 458,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Clean",
            "desc": "Cleans up all objects in the trove. This is\nsimilar to calling `Remove` on each object\nwithin the trove. The ordering of the objects\nremoved is _not_ guaranteed.\n\n```lua\ntrove:Clean()\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 478,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "WrapClean",
            "desc": "Returns a function that wraps the trove's `Clean()`\nmethod. Calling the returned function will clean up\nthe trove.\n\nThis is often useful in contexts where functions\nare the primary mode for cleaning up an environment,\nsuch as in many \"observer\" patterns.\n\n```lua\nlocal cleanup = trove:WrapClean()\n\n-- Sometime later...\ncleanup()\n```\n\n```lua\n-- Common observer pattern example:\nsomeObserver(function()\n\tlocal trove = Trove.new()\n\t-- Foo\n\treturn trove:WrapClean()\nend)\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 520,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "AttachToInstance",
            "desc": "Attaches the trove to a Roblox instance. Once this\ninstance is removed from the game (parent or ancestor's\nparent set to `nil`), the trove will automatically\nclean up.\n\nThis inverses the ownership of the Trove object, and should\nonly be used when necessary. In other words, the attached\ninstance dictates when the trove is cleaned up, rather than\nthe trove dictating the cleanup of the instance.\n\n:::caution\nWill throw an error if `instance` is not a descendant\nof the game hierarchy.\n:::\n\n```lua\ntrove:AttachToInstance(somePart)\ntrove:Add(function()\n\tprint(\"Cleaned\")\nend)\n\n-- Destroying the part will cause the trove to clean up, thus \"Cleaned\" printed:\nsomePart:Destroy()\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptConnection"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 586,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Alias for `trove:Clean()`.\n\n```lua\ntrove:Destroy()\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 607,
                "path": "modules/trove/init.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "Trackable",
            "desc": "Represents all trackable objects by Trove.",
            "lua_type": "Instance | RBXScriptConnection | ConnectionLike | PromiseLike | thread | ((...any) -> ...any) | Destroyable | DestroyableLowercase | Disconnectable | DisconnectableLowercase",
            "source": {
                "line": 32,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "ConnectionLike",
            "desc": "",
            "fields": [
                {
                    "name": "Connected",
                    "lua_type": "boolean",
                    "desc": ""
                },
                {
                    "name": "Disconnect",
                    "lua_type": "(self) -> ()",
                    "desc": ""
                }
            ],
            "source": {
                "line": 50,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "SignalLike",
            "desc": "",
            "fields": [
                {
                    "name": "Connect",
                    "lua_type": "(self, callback: (...any) -> ...any) -> ConnectionLike",
                    "desc": ""
                },
                {
                    "name": "Once",
                    "lua_type": "(self, callback: (...any) -> ...any) -> ConnectionLike",
                    "desc": ""
                }
            ],
            "source": {
                "line": 61,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "PromiseLike",
            "desc": "",
            "fields": [
                {
                    "name": "getStatus",
                    "lua_type": "(self) -> string",
                    "desc": ""
                },
                {
                    "name": "finally",
                    "lua_type": "(self, callback: (...any) -> ...any) -> PromiseLike",
                    "desc": ""
                },
                {
                    "name": "cancel",
                    "lua_type": "(self) -> ()",
                    "desc": ""
                }
            ],
            "source": {
                "line": 73,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Constructable",
            "desc": "",
            "lua_type": "{ new: (A...) -> T } | (A...) -> T",
            "source": {
                "line": 83,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Destroyable",
            "desc": "",
            "fields": [
                {
                    "name": "disconnect",
                    "lua_type": "(self) -> ()",
                    "desc": ""
                }
            ],
            "source": {
                "line": 90,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "DestroyableLowercase",
            "desc": "",
            "fields": [
                {
                    "name": "disconnect",
                    "lua_type": "(self) -> ()",
                    "desc": ""
                }
            ],
            "source": {
                "line": 99,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "Disconnectable",
            "desc": "",
            "fields": [
                {
                    "name": "disconnect",
                    "lua_type": "(self) -> ()",
                    "desc": ""
                }
            ],
            "source": {
                "line": 108,
                "path": "modules/trove/init.luau"
            }
        },
        {
            "name": "DisconnectableLowercase",
            "desc": "",
            "fields": [
                {
                    "name": "disconnect",
                    "lua_type": "(self) -> ()",
                    "desc": ""
                }
            ],
            "source": {
                "line": 117,
                "path": "modules/trove/init.luau"
            }
        }
    ],
    "name": "Trove",
    "desc": "A Trove is helpful for tracking any sort of object during\nruntime that needs to get cleaned up at some point.",
    "source": {
        "line": 169,
        "path": "modules/trove/init.luau"
    }
}