Skip to main content

Register and call typed hooks

Defining a hook contract with TypeScript allows hookable to provide type guidance for hook names and callback signatures. By using createHooks, you can instantiate a typed Hookable object that ensures handlers registered with hook and events triggered by callHook adhere to your defined interface.

Registering multiple handlers

When multiple handlers are registered for the same hook name, hookable executes them sequentially in the order they were added. Each handler must be a function that returns void or a Promise<void>. The callHook method returns a promise that resolves only after all registered handlers for that hook have completed their execution.

import { createHooks } from "hookable";

interface MyHooks {
"start": () => void | Promise<void>;
}

async function runExample() {
const hooks = createHooks<MyHooks>();
const trace: string[] = [];
const handlerOne = () => {
trace.push("handler1");
};
const handlerTwo = () => {
trace.push("handler2");
};

hooks.hook("start", handlerOne);
hooks.hook("start", handlerTwo);
await hooks.callHook("start");

console.assert(trace[0] === "handler1");
console.assert(trace[1] === "handler2");
}

await runExample();

Managing handler lifecycles

The hook method returns an unregister function that allows you to remove a specific handler when it is no longer needed. Invoking this returned function ensures that the associated callback is not executed during subsequent callHook invocations for that hook name.

import { createHooks } from "hookable";

interface AppHooks {
"app:shutdown": () => void | Promise<void>;
}

async function runUnregisterExample() {
const hooks = createHooks<AppHooks>();
const trace: string[] = [];
const temporaryHandler = () => {
trace.push("temporary handler was called");
};

const unregister = hooks.hook("app:shutdown", temporaryHandler);
await hooks.callHook("app:shutdown");
console.assert(trace.length === 1);

unregister();
await hooks.callHook("app:shutdown");
console.assert(trace.length === 1);
}

await runUnregisterExample();

Execution behavior

Triggering a hook with callHook is an asynchronous operation. If any registered handler throws an error or returns a promise that rejects, the promise returned by callHook will also reject. When no handler throws or rejects, callHook awaits each registered handler in registration order.

The type parameters provided to createHooks serve as guidance during development to help match hook names with their expected callback signatures. While these types assist in writing correct code, they do not change the runtime behavior of the hook execution. Handlers should focus on producing observable effects, as the system is designed for sequential execution of logic rather than aggregating return values from callbacks.