npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-typed-events

v3.0.0

Published

Zero dependency strongly typed event emitters for TypeScript

Downloads

16,302

Readme

ts-typed-events

Zero dependency strongly typed event emitters for TypeScript.

Purpose

Using Node's EventEmitter class is nice; it's a useful software engineering paradigm. However in TypeScript you lose out on TypeScript's robust type checking when publishing and subscribing to events; or you must write very brittle function overloads for every function: on, off, once, emit, etc. This is annoying and tedious.

The aim of this is to leverage TypeScript's generics to allow for build-time type checking. We also move the events into their own functions, so you don't have to inherit or mixin any of our classes; just use these single Event emitters. It is also un-opinionated, exposing functional and object oriented building blocks in this library so you can use it best works in your project.

Examples

Simple usage

With classes

import { Event } from 'ts-typed-events';

const event = new Event();

event.on(() => {
  console.log('The event triggered!');
});

event.emit(); // prints: `The event triggered!`

Or with functions

import { createEmitter } from 'ts-typed-events';

const { emit, event } = createEmitter();

event.on(() => {
  console.log('The event triggered!');
});

emit(); // prints: `The event triggered!`

Strongly typed events

const event = new Event<string>();

event.on((str) => {
  console.log('hey we got the string:', str);
});

// this emit function requires an argument of type `string`
event.emit('some string'); // prints `hey we got the string: some string`

async/await usage

You can register event listeners via traditional callbacks, or if no callback is supplied to event.once(), a Promise is returned that you can await.

const event = new Event<number>();

// emit the event in 1 second
setTimeout(() => event.emit(1337), 1000);

const emitted = await event.once();
console.log('1 sec later we got', emitted);
// printed: `1 sec later we got 1337`

Note: async only works with once, not on as that can trigger multiple times.

Multiple callbacks

const event = new Event<'pizza' | 'ice cream'>();

event.on((food) => console.log('I like', food));
event.on((badFood) => console.log(badFood, 'is bad for me!'));

event.emit('pizza');
// printed: `I like pizza`, followed by: `pizza is bad for me!`

Removing callbacks

const event = new Event();

const callback = () => { throw new Error(`I don't want to be called`); };
event.on(callback);
event.off(callback);

// The callback was removed, so the Error in the callback is not thrown
const emitted = event.emit();
console.log('were any callbacks invoked during the emit?', emitted);
// printed: `were any callbacks invoked during the emit? false`

Combining this within Classes

As this module intends to replace the "built-in" EventEmitter class, you can use it much more freely because it is not a class you must inherit like with the EventEmitter class. To replicate similar functionality you can make each event a separate member variable of your.

class Dog {
  private timesBarked = 0;
  // NOTE: the emitter is private here, and the event is public.
  // This allows the class to completely control when it emits.
  private emitBarked = createEmitter();

  public barked = this.emitBarked.event;

  public bark() {
    this.timesBarked += 1;
    this.emitBarked();
  }
}

const dog = new Dog();
dog.barked.on(() => console.log('The dog barked!'));
dog.bark(); // prints: `The dog barked!`;

createEmitter alternative Syntax

The method createEmitter returns an emitter function, with the event and itself as properties. This makes the above examples when used with destructuring look clean. However you can choose not to destructure it as well:

const emit = createEmitter<'something' | undefined>();

emit.event.on((something) => {
  console.log('did we get something?:', something);
});

emit(); // prints `did we get something?: undefined`
emit.emit('something'); // prints `did we get something?: something`

// the emitter has access to itself via the `emit` key as well
console.log(emit === emit.emit); // prints `true`

Sealed Events

The Event class has exposed the member function emit, which means any bit of code that can register as a listener on your event, can also force that event to emit.

Often you do not want to trust bits of code with that responsibility. For that common use case this module exposes an alternative type of events and API to generate Events called SealedEvents that cannot self emit, and a separate emitter function to handle the emitting logic.

import { createEmitter, SealedEvent } from 'ts-typed-events';

const { event, emit } = createEmitter<BigInt>();

event instanceof SealedEvent; // true
'emit' in event; // === false

event.on((int) => {
  console.log('Emitted BigInt:', int);
});

emit(1337n); // prints: 'Emitted BigInt: 1337'

Note: you can also use createEventEmitter if you wish the event type to be instanceof Event. Keep in mind that regular Event instances have access to their emitter via .emit. This module exposes both variants for API uniformity.

Other Notes

This library leverages TypeScript's interfaces and generics very heavily to do type checking at every point. If you plan to use this with regular JavaScript most of this library's features are lost to you. However do not let that deter you from trying this, or TypeScript!

Docs

All the functions are documented using jsdoc style comments, so your favorite IDE should pick up the documentation cleanly.

However, if you prefer external docs, they are available online here: https://jacobfischer.github.io/ts-typed-events/