jaset
v2.0.0
Published
A simple, type-safe implementation of EventTarget with zero dependencies and select enhancements for a better developer experience.
Downloads
191
Maintainers
Readme
jaset (just a strict event target) is a simple, type-safe implementation of EventTarget with zero dependencies and select enhancements✨ for a better developer experience — including short-form method names, event muting, an optional wildcard event type and more.
Installation
npm i jasetSee Package exports for a list of all package exports.
Usage
You can use jaset just like you would use EventTarget with the addition of providing an event map.
import EventTarget, { type EventMap } from "jaset"
class MyEventTarget extends EventTarget<MyEventMap> {}
type MyEventMap = EventMap<{
"my-event": MyEvent
}>
const myEventTarget = new MyEventTarget()
// ✅ "my-event" is a valid event type: No complaints.
myEventTarget.on("my-event", (event) => {
// ✅ event is `MyEvent` and not just any `Event`.
})
// ⛔️ "foo" is not a valid event type: TypeScript error.
myEventTarget.on("foo", (event) => {
// ...
})Define custom events
jaset supports events that extend Event.
class MyEvent extends Event {
constructor() {
super("my-event")
}
}[!IMPORTANT] If you implement multiple events that share the same interface, you must add an explicit
typeproperty to ensure maximum type-safety. Otherwise, the TypeScript compiler will not be able to distinguish between these events.Example:
class MyEvent1 extends Event {
readonly type = "my-event-1" as const
constructor() {
super("my-event-1")
}
}
class MyEvent2 extends Event {
readonly type = "my-event-2" as const
constructor() {
super("my-event-2")
}
}Use CustomEvent instead of Event
If you prefer to use CustomEvent, just use it directly in your event map.
type MyEventMap = EventMap<{
"my-event": CustomEvent<number>
}>Add event listeners
myEventTarget.on("my-event", onMyEvent)
myEventTarget.addEventListener("my-event", onMyEvent)
function onMyEvent(event: MyEvent) {
// ...
}[!NOTE] Just like it is the case with
EventTarget, an attempt to add the same event listener twice will be ignored without throwing an error.
Once
Once event listeners are listeners that are only invoked once and then removed.
myEventTarget.once("my-event", onMyEvent)
myEventTarget.addEventListener("my-event", onMyEvent, { once: true })Dispatch events
myEventTarget.emit(new MyEvent())
myEventTarget.dispatchEvent(new MyEvent())Remove event listeners
myEventTarget.off("my-event", onMyEvent)
myEventTarget.removeEventListener("my-event", onMyEvent)Remove all event listeners ✨
You can remove all event listeners for a specific event type.
myEventTarget.clear("my-event")Event listener map ✨
A ReadonlyMap of event types to event listeners is exposed via eventListeners.
[!NOTE] This read-only map only contains active event listeners. Event types are removed once their last event listener is removed. Wildcard event listeners, if present, are also included in this map with an event type of
"*".
Mute events ✨
You can mute all events of a certain type, allowing you to prevent all event listeners from invoking until you unmute.
// Mute. 🔇
myEventTarget.mute("my-event")
// Ignored. 🙈
myEventTarget.emit(new MyEvent())
// Unmute. 🔊
myEventTarget.unmute("my-event")A ReadonlySet of explicitly muted event types is available via mutedEventTypes:
myEventTarget.mutedEventTypes
// ReadonlySet<keyof MyEventMap>[!NOTE] Muting is independent of event listeners. An event type stays muted until you unmute it, even if it currently has no listeners. Muting the wildcard event type (
"*") mutes all events, including event types that have no listeners yet or are dispatched in the future; this does not populatemutedEventTypes. Unmuting the wildcard event type unmutes all events.
Wildcard event type ✨
With the optional wildcard event type ("*") you can reference all event types at once. This is supported by all jaset methods, except emit() and dispatchEvent().
myEventTarget.on("*", onAnyEvent)
myEventTarget.off("*", onAnyEvent)
myEventTarget.clear("*")
myEventTarget.mute("*")
myEventTarget.unmute("*")
function onAnyEvent(event: MyEvent | MyOtherEvent) {
// Eyes on everyone. 👀
}[!IMPORTANT] The wildcard event type is disallowed by default for cleaner IntelliSense suggestions. You can allow it by setting the generic type parameter
AllowWildcardEventTypetotrue.Example:
import EventTarget from "jaset"
class MyEventTarget extends EventTarget<
MyEventMap,
/* AllowWildcardEventType */ true
> {}Combine event maps ✨
You can combine multiple event maps when extending the jaset event target.
import EventTarget, { type EventMap } from "jaset"
class MyEventTarget<
MyEventMap extends EventMap<MyEventMap>,
> extends EventTarget<BaseEventMap & MyEventMap> {}
type BaseEventMap = EventMap<{
"my-event": MyEvent
}>
class MyEventTargetExtension extends MyEventTarget<ExtensionEventMap> {}
type ExtensionEventMap = EventMap<{
"my-other-event": MyOtherEvent
}>
const myEventTarget = new MyEventTargetExtension()
myEventTarget.on("my-event", () => {}) // ✅
myEventTarget.on("my-other-event", () => {}) // ✅
myEventTarget.on("foo", () => {}) // ⛔️Combining event maps can be useful when designing an interface where clients should be able to extend the base event map you define.
You can prevent clients from being able to override entries from the base event map by utilizing Omit:
class MyEventTarget<
MyEventMap extends Omit<EventMap<MyEventMap>, keyof BaseEventMap>,
> extends EventTarget<BaseEventMap & MyEventMap> {}Package exports
The jaset package exports the following:
Event target class
import EventTarget from "jaset" // or import { EventTarget } from "jaset"Map of event types to events (type only)
import type { EventMap } from "jaset"Used for defining custom event maps.
type MyEventMap = EventMap<{ "my-event": MyEvent }>When the type parameter of
EventMapis omitted, the event map becomesRecord<string, Event>.
