Centralizing events in foxes.ts
Best practice: define all event names and payload types in a single file (e.g. foxes.ts) and import from there. That gives you one source of truth, type safety, and easier refactors.
Why centralize?
- Single source of truth — Event names live in one place; no typos or duplicated strings across the app.
- Typed payloads — Use an EventMap so
emitandonare type-checked. - Easier refactors — Rename or add events in one file; TypeScript will flag every usage.
- Discoverability — New developers see all app events in one file.
Example: foxes.ts
Create a file (e.g. src/foxes.ts or lib/foxes.ts) that defines your event map and optionally exports typed channels or helpers.
ts
import { Fox } from "fox-events";
// 1. Define the event map: event name → payload type (use void for no payload)
export type AppEvents = {
"user:login": { userId: string; email?: string };
"user:logout": void;
"app:ready": { version: string };
"app:command": { action: "reload" | "back" };
};
// 2. Optional: typed helpers so you don't repeat the generic everywhere
export const emit = Fox.emit<AppEvents>;
export const on = Fox.on<AppEvents>;
export const once = Fox.once<AppEvents>;
export const emitAsync = Fox.emitAsync<AppEvents>;
// 3. Optional: pre-created channels if you prefer channel API over static
export const channels = {
userLogin: Fox.channel<AppEvents["user:login"]>("user:login"),
userLogout: Fox.channel<AppEvents["user:logout"]>("user:logout"),
appReady: Fox.channel<AppEvents["app:ready"]>("app:ready"),
appCommand: Fox.channel<AppEvents["app:command"]>("app:command"),
} as const;Using it in the app
With static API (emit/on/once):
ts
import { emit, on, once } from "@/foxes";
// Typed: payload must match AppEvents["user:login"]
emit("user:login", { userId: "u-1" });
on("app:ready", (payload) => {
console.log(payload.version);
});
const payload = await once("user:login");With channels:
ts
import { channels } from "@/foxes";
channels.userLogin.emit({ userId: "u-1" });
channels.appReady.on((payload) => console.log(payload.version));
const payload = await channels.userLogin.once();If you only export the type:
ts
import { Fox } from "fox-events";
import type { AppEvents } from "@/foxes";
Fox.emit<AppEvents>("user:login", { userId: "u-1" });
Fox.on<AppEvents>("app:ready", (payload) => {});Naming conventions
- Use a domain:action style so names stay consistent and easy to search (e.g.
user:login,app:ready,cart:item-added). - Keep the event map in sync: add new events in
foxes.tsfirst, then use them.
Summary
| Approach | Use when |
|---|---|
Export AppEvents only | You want one central type and are fine with Fox.emit<AppEvents>(...) everywhere. |
Export emit / on / once | You want a drop-in replacement for Fox with types already applied. |
Export channels | You prefer the channel API and want named channels (e.g. channels.userLogin) from one place. |
Putting everything in foxes.ts keeps event names and payloads consistent and makes the rest of the app easier to maintain.