{"id":16885294,"url":"https://github.com/niklasramo/eventti","last_synced_at":"2026-03-27T02:12:56.846Z","repository":{"id":57686191,"uuid":"485548016","full_name":"niklasramo/eventti","owner":"niklasramo","description":"A predictable event emitter for pragmatists, written in TypeScript.","archived":false,"fork":false,"pushed_at":"2025-03-09T23:21:08.000Z","size":622,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-30T00:01:38.242Z","etag":null,"topics":["emitter","event","event-emitter","events","pubsub","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/niklasramo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["niklasramo"]}},"created_at":"2022-04-25T22:00:54.000Z","updated_at":"2025-03-09T23:20:30.000Z","dependencies_parsed_at":"2024-02-01T00:24:47.457Z","dependency_job_id":"ecd532b6-14ff-4ed5-bb1f-34eb841df394","html_url":"https://github.com/niklasramo/eventti","commit_stats":{"total_commits":52,"total_committers":1,"mean_commits":52.0,"dds":0.0,"last_synced_commit":"ae21862d4b1ec5661b3ed82e34e8b546d1e0b381"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklasramo%2Feventti","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklasramo%2Feventti/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklasramo%2Feventti/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklasramo%2Feventti/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/niklasramo","download_url":"https://codeload.github.com/niklasramo/eventti/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248080331,"owners_count":21044484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emitter","event","event-emitter","events","pubsub","typescript"],"created_at":"2024-10-13T16:34:32.306Z","updated_at":"2026-03-27T02:12:56.826Z","avatar_url":"https://github.com/niklasramo.png","language":"JavaScript","funding_links":["https://github.com/sponsors/niklasramo"],"categories":[],"sub_categories":[],"readme":"# Eventti\n\nA _predictable_ event emitter for pragmatists, written in TypeScript.\n\n- 🔮 Predictable behaviour.\n- 🎯 Hits the sweet spot between features, size and performance.\n- 🎁 Small footprint (568 bytes minified and gzipped to be exact).\n- ⚡ Highly optimized and stable performance across browsers.\n- 🤖 Extensively tested.\n- 🍭 No runtime dependencies, what you see is what you get.\n- 💝 Free and open source, MIT Licensed.\n\n## Why another event emitter?\n\nDevs just _love_ to write their own event emitters, don't they? 🙋‍♂️ I've been guilty of writing a few myself, and I've also used many existing implementations of which there certainly is no shortage of.\n\nHowever, there's always been one aspect that irks me in most of them, and that's the handling of duplicate event listeners. It's inconsistent. In some implementations duplicate listeners are not allowed (and they might be just silently ignored) while in others they are allowed, but then you can't remove a _specific_ listener anymore, because you don't know which one of the duplicates you're removing. Depending on the implementation it might be the first, last or all matching listeners.\n\n```ts\n// A very contrived example, but you get the point.\nconst ee = new YetAnotherEventEmitter();\nlet counter = 1;\nconst increment = () =\u003e void counter += 1;\nconst square = () =\u003e void counter *= counter;\nee.on('test', increment);\nee.on('test', square);\nee.on('test', increment);\nee.off('test', increment); // 🎲\nee.emit('test'); // counter === 1 or 2 or 4\n```\n\nTo rectify this flaw in API design, Eventti follows in the footsteps of `setTimeout()`/`clearTimeout()` (and other similar Web APIs) by assigning and returning a unique id for each listener. No more guessing which listener you're removing.\n\n```ts\nimport { Emitter } from 'eventti';\nconst ee = new Emitter();\nlet counter = 1;\nconst increment = () =\u003e void counter += 1;\nconst square = () =\u003e void counter *= counter;\nconst id1 = ee.on('test', increment);\nconst id2 = ee.on('test', square);\nconst id3 = ee.on('test', increment);\nee.off('test', id3); // ✅\nee.emit('test'); // counter === 4\n```\n\nAdditionally, you can provide the listener id manually _and_ define strategy for handling duplicate ids. This is especially useful when you want to update an existing listener with a new one _without changing it's index in the listener queue_.\n\n```ts\nimport { Emitter } from 'eventti';\nconst ee = new Emitter({ dedupe: 'update' });\nee.on('test', () =\u003e console.log('foo'), 'idA');\nee.on('test', () =\u003e console.log('bar'), 'idB');\n// Update the listener for idA.\nee.on('test', () =\u003e console.log('baz'), 'idA');\nee.emit('test');\n// -\u003e baz\n// -\u003e bar\n```\n\nFinally, I hope this is the last event emitter I'll ever need to write 🤞.\n\n## Performance\n\nRegarding performance, Eventti is fine-tuned to be as fast as possible _with the feature set it provides_.\n\nListeners and their ids are stored in a `Map\u003cid, listener\u003e` instead of an array (the common convention), due to which Eventti loses a bit in performance when it comes to adding listeners. It's hard to beat a simple `array.push()`.\n\nHowever, emitting speed is on par with the fastest of emitters due to an optimization Eventti applies: the listeners are cached in an array, which is invalidated any time a listener is removed. This way we don't have to _always_ clone the listeners on emit, most of the time we can go straight to looping the cached array.\n\nWhere things get interesting is when we start removing listeners. While most emitters need to loop an array of listeners to find the matching listener(s), Eventti can just delete the listener from the `Map` by the listener id. This is a huge performance boost when you have a lot of listeners.\n\nIn practice, Eventti and most other emitters are so fast that you don't need to worry about performance. But if you're interested in the numbers, we have some [benchmarks](./benchmarks/), which you can run with `npm run bench`.\n\n## Getting started\n\nThe library is provided as an ECMAScript module (ESM) and a CommonJS module (CJS).\n\n### Node\n\n```\nnpm install eventti\n```\n\n```ts\nimport { Emitter } from 'eventti';\nconst emitter = new Emitter();\n```\n\n### Browser\n\n```html\n\u003cscript type=\"importmap\"\u003e\n  {\n    \"imports\": {\n      \"eventti\": \"https://cdn.jsdelivr.net/npm/eventti@4/dist/index.js\"\n    }\n  }\n\u003c/script\u003e\n\u003cscript type=\"module\"\u003e\n  import { Emitter } from 'eventti';\n  const emitter = new Emitter();\n\u003c/script\u003e\n```\n\n## Usage\n\n```ts\nimport { Emitter } from 'eventti';\n\n// Define emitter's events (if using TypeScript).\n// Let the key be the event name and the value\n// be the listener callback signature.\ntype Events = {\n  a: (msg: string) =\u003e void;\n  b: (str: string, num: number) =\u003e void;\n};\n\n// Create an emitter instance.\nconst emitter = new Emitter\u003cEvents\u003e();\n\n// Add listeners to events.\nconst idA = emitter.on('a', (msg) =\u003e console.log(msg));\nconst idB = emitter.on('b', (...args) =\u003e console.log(...args));\n\n// Add a one-off listener to an event.\nemitter.once('a', (msg) =\u003e console.log('once: ' + msg));\n\n// Emit events.\nemitter.emit('a', 'foo');\n// -\u003e foo\n// -\u003e once: foo\nemitter.emit('a', 'foo');\n// -\u003e foo\nemitter.emit('b', 'bar', 5000);\n// -\u003e bar 5000\n\n// Count \"a\" event listeners.\nemitter.listenerCount('a'); // -\u003e 1\n\n// Count all listeners.\nemitter.listenerCount(); // -\u003e 2\n\n// Remove listeners.\nemitter.off('a', idA);\nemitter.off('b', idB);\n```\n\n## Listener ids and dedupe modes\n\nThe founding feature of Eventti is that every listener is assigned with an id. The id can be any value except `undefined`. By default Eventti uses `Symbol()` to create unique ids, but you can provide your own function if you want to use something else _and_ you can also provide the id manually via `.on()` and `.once()` methods.\n\nNow the question is, what should happen when you try to add a listener with an id that already exists? Well, it's up to you and Eventti allows you to choose from four different options what the behaviour should be.\n\n### dedupe: \"add\"\n\nWhen `dedupe` is set to \"add\" (which it is by default) the existing listener (matching the id) will be first completely removed and then the new listener will be appended to the listener queue.\n\n```ts\nimport { Emitter, EmitterDedupe } from 'eventti';\n\nconst emitter = new Emitter({ dedupe: EmitterDedupe.ADD });\n\nemitter.on('a', () =\u003e console.log('foo 1'), 'foo');\nemitter.on('a', () =\u003e console.log('bar'), 'bar');\nemitter.on('a', () =\u003e console.log('foo 2'), 'foo');\n\nemitter.emit('a');\n// -\u003e bar\n// -\u003e foo 2\n```\n\n### dedupe: \"update\"\n\nWhen `dedupe` is set to \"update\" the existing listener (matching the id) will be replaced with new listener while keeping the listener at the same index in the listener queue.\n\n```ts\nimport { Emitter, EmitterDedupe } from 'eventti';\n\nconst emitter = new Emitter({ dedupe: EmitterDedupe.UPDATE });\n\nemitter.on('a', () =\u003e console.log('foo 1'), 'foo');\nemitter.on('a', () =\u003e console.log('bar'), 'bar');\nemitter.on('a', () =\u003e console.log('foo 2'), 'foo');\n\nemitter.emit('a');\n// -\u003e foo 2\n// -\u003e bar\n```\n\n### dedupe: \"ignore\"\n\nWhen `dedupe` is set to \"ignore\" the new listener is simply ignored.\n\n```ts\nimport { Emitter, EmitterDedupe } from 'eventti';\n\nconst emitter = new Emitter({ dedupe: EmitterDedupe.IGNORE });\n\nemitter.on('a', () =\u003e console.log('foo 1'), 'foo');\nemitter.on('a', () =\u003e console.log('bar'), 'bar');\nemitter.on('a', () =\u003e console.log('foo 2'), 'foo');\n\nemitter.emit('a');\n// -\u003e foo 1\n// -\u003e bar\n```\n\n### dedupe: \"throw\"\n\nWhen `dedupe` is set to \"throw\" an error is thrown.\n\n```ts\nimport { Emitter, EmitterDedupe } from 'eventti';\n\nconst emitter = new Emitter({ dedupe: EmitterDedupe.THROW });\n\nemitter.on('a', () =\u003e console.log('foo 1'), 'foo');\nemitter.on('a', () =\u003e console.log('bar'), 'bar');\nemitter.on('a', () =\u003e console.log('foo 2'), 'foo'); // throws an error\n```\n\n### Changing dedupe mode\n\nYou can change the `dedupe` mode at any point after instantiaiting the emitter. Just directly set the mode via the emitter's `dedupe` property.\n\n```ts\nimport { Emitter, EmitterDedupe } from 'eventti';\n\nconst emitter = new Emitter();\n\nemitter.dedupe = EmitterDedupe.THROW;\n```\n\n## Tips and tricks\n\n### Mimicking the _classic_ event emitter API\n\nEventti's API is a bit different from most other event emitters, but you can easily mimic the _classic_ API (where you remove listeners based on the callback) by using the `getId` option. This way you can use the listener callback as the listener id by default and remove listeners based on the callback. But do note that this way duplicate listeners can't be added, which may or may not be what you want.\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter({\n  // Decide what to do with duplicate listeners by default.\n  dedupe: 'throw',\n  // Use the listener callback as the listener id.\n  getId: (listener) =\u003e listener,\n});\n\nconst listener = () =\u003e {};\n\n// Now the listener itself is used as the id automatically.\nconst idA = emitter.on('a', listener);\nconsole.log(idA === listener); // -\u003e true\n\n// And you can remove it without an explicit id.\nemitter.off('a', listener);\n\n// And duplicate listeners can't be added.\nemitter.on('a', listener);\nemitter.on('a', listener); // throws an error\n```\n\n### Ergonomic unbinding\n\nEventti's API is designed to be explicit and predictable, but sometimes you might want a bit more ergonomic API by returning a function from the `.on()` and `.once()` methods which you can use to unbind the listener.\n\nHere's a recipe for that:\n\n```ts\nimport { Emitter, Events, EventListenerId, EmitterOptions } from 'eventti';\n\nclass ErgonomicEmitter\u003cT extends Events\u003e extends Emitter\u003cT\u003e {\n  constructor(options?: EmitterOptions) {\n    super(options);\n  }\n\n  on\u003cEventName extends keyof T\u003e(\n    eventName: EventName,\n    listener: T[EventName],\n    listenerId?: EventListenerId,\n  ): EventListenerId {\n    const id = super.on(eventName, listener, listenerId);\n    return () =\u003e this.off(eventName, id);\n  }\n\n  once\u003cEventName extends keyof T\u003e(\n    eventName: EventName,\n    listener: T[EventName],\n    listenerId?: EventListenerId,\n  ): EventListenerId {\n    const id = super.once(eventName, listener, listenerId);\n    return () =\u003e this.off(eventName, id);\n  }\n}\n\nconst emitter = new ErgonomicEmitter();\nconst unbind = emitter.on('a', () =\u003e console.log('foo'));\nunbind(); // removes the listener\n```\n\nDo note that this breaks the API contract as now you can't use the return value of `.on()` and `.once()` methods anymore to remove the listeners with `.off` method. But if you're okay with that, this is a nice way to make the API more ergonomic.\n\n### Scheduled callback with deduplication (debounce / coalesce)\n\nWhen scheduling work to happen later, you can use a stable listener id with `once()` to ensure only **one** callback is registered, regardless of how many times the function is called before the flush.\n\nThe `dedupe` mode controls what happens when the same ID is used again:\n\n- `'add'` (default) — **latest** callback wins, repositioned to end of queue.\n- `'update'` — **latest** callback wins, original position preserved.\n- `'ignore'` — **first** callback wins, original position preserved.\n\nFor a simple single-event debounce where listener order doesn't matter, the default `'add'` works fine.\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter();\nconst flushId = 'search:flush';\nlet pending = false;\n\nexport function requestSearch(query: string, run: (query: string) =\u003e void) {\n  emitter.once('flush', () =\u003e run(query), flushId);\n\n  if (!pending) {\n    pending = true;\n    setTimeout(() =\u003e {\n      pending = false;\n      emitter.emit('flush');\n    }, 250);\n  }\n}\n```\n\n### Hot-swap a listener without changing its position (stable slot)\n\nWhen you want to update a listener's implementation while keeping it at the same position in the listener queue, use `dedupe: 'update'` with a stable id.\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter({ dedupe: 'update' });\n\nemitter.on('request', () =\u003e console.log('log'), 'hook:log');\nemitter.on('request', () =\u003e console.log('auth v1'), 'hook:auth');\nemitter.on('request', () =\u003e console.log('metrics'), 'hook:metrics');\n\n// Replace auth hook, but keep it between \"log\" and \"metrics\".\nemitter.on('request', () =\u003e console.log('auth v2'), 'hook:auth');\n```\n\n### Building a ticker\n\nEventti is a great fit for building a ticker, which is a common use case for event emitters. Here's a simple example of how you could build a `requestAnimationFrame` ticker.\n\n```ts\nimport { Emitter, EmitterOptions, EventListenerId } from 'eventti';\n\nclass Ticker {\n  private tickId: number | null;\n  private emitter: Emitter\u003c{ tick: (time: number) =\u003e void }\u003e;\n\n  constructor(options?: EmitterOptions) {\n    this.tickId = null;\n    this.emitter = new Emitter(options);\n  }\n\n  on(listener: (time: number) =\u003e void, listenerId?: EventListenerId): EventListenerId {\n    return this.emitter.on('tick', listener, listenerId);\n  }\n\n  once(listener: (time: number) =\u003e void, listenerId?: EventListenerId): EventListenerId {\n    return this.emitter.once('tick', listener, listenerId);\n  }\n\n  off(listenerId?: EventListenerId): void {\n    this.emitter.off('tick', listenerId);\n  }\n\n  start(): void {\n    if (this.tickId !== null) return;\n    const tick = (time: number) =\u003e {\n      this.tickId = requestAnimationFrame(tick);\n      if (time) this.emitter.emit('tick', time);\n    };\n    tick(0);\n  }\n\n  stop(): void {\n    if (this.tickId === null) return;\n    cancelAnimationFrame(this.tickId);\n    this.tickId = null;\n  }\n}\n\nconst ticker = new Ticker();\n\nconst idA = ticker.on(() =\u003e console.log('tick a'));\nconst idB = ticker.on(() =\u003e console.log('tick b'));\n\nticker.off(idB);\n\nticker.start();\n// -\u003e tick a\n// -\u003e tick a\n// -\u003e tick a\n// ...\n\nticker.stop();\n```\n\nIf you want a more advanced and battle-tested ticker you might want to check out [tikki](https://github.com/niklasramo/tikki), a ticker implementation built on top of Eventti.\n\n## Emitter API\n\n- [Constructor](#constructor)\n- [on( eventName, listener, [ listenerId ] )](#emitteron)\n- [once( eventName, listener, [ listenerId ] )](#emitteronce)\n- [off( [ eventName ], [ listenerId ] )](#emitteroff)\n- [emit( eventName, [ ...args ] )](#emitteremit)\n- [listenerCount( [ eventName ] )](#emitterlistenercount)\n- [Types](#types)\n\n### Constructor\n\n`Emitter` is a class which's constructor accepts an optional [`EmitterOptions`](#emitteroptions) object with the following properties:\n\n- **dedupe**\n  - Defines how a duplicate event listener id is handled:\n    - `\"add\"`: the existing listener (of the id) is removed and the new listener is appended to the event's listener queue.\n    - `\"update\"`: the existing listener (of the id) is replaced with the new listener without changing the index of the listener in the event's listener queue.\n    - `\"ignore\"`: the new listener is silently ignored and not added to the event.\n    - `\"throw\"`: as the name suggests an error will be thrown.\n  - Accepts: [`EmitterDedupe`](#emitterdedupe).\n  - Optional, defaults to `\"add\"` if omitted.\n- **getId**\n  - A function which is used to get the listener id for a listener callback. By default Eventti uses `Symbol()` to create unique ids, but you can provide your own function if you want to use something else. Receives the listener callback as the first (and only) argument.\n  - Accepts: `(listener: EventListener) =\u003e EventListenerId`.\n  - Optional, defaults to `() =\u003e Symbol()` if omitted.\n\n```ts\nimport { Emitter, EmitterDedupe } from 'eventti';\n\n// Define emitter's events (if using TypeScript).\n// Let the key be the event name and the value\n// be the listener callback signature.\ntype Events = {\n  a: (msg: string) =\u003e void;\n  b: (str: string, num: number) =\u003e void;\n};\n\n// Create emitter instance.\nconst emitterA = new Emitter\u003cEvents\u003e();\n\n// Create emitter instance with options.\nlet _id = Number.MIN_SAFE_INTEGER;\nconst emitterB = new Emitter\u003cEvents\u003e({\n  dedupe: EmitterDedupe.THROW,\n  getId: () =\u003e ++_id,\n});\n\n// You can read and modify the `dedupe` setting\n// freely. It's okay to change it's value whenever\n// you want.\nemitterB.dedupe; // -\u003e \"throw\"\nemitterB.dedupe = EmitterDedupe.IGNORE;\n\n// You can read and modify the `getId` setting freely.\n// It's okay to change it's value whenever you want.\nemitterB.getId = () =\u003e Symbol();\n```\n\n### emitter.on()\n\nAdd a listener to an event.\n\n**Syntax**\n\n```\nemitter.on( eventName, listener, [ listenerId ] )\n```\n\n**Parameters**\n\n1. **eventName**\n   - The name of the event you want to add a listener to.\n   - Accepts: [`EventName`](#eventname).\n2. **listener**\n   - A listener function that will be called when the event is emitted.\n   - Accepts: [`EventListener`](#eventlistener).\n3. **listenerId**\n   - The id for the listener. If not provided, the id will be generated by the `emitter.getId` method.\n   - Accepts: [`EventListenerId`](#eventlistenerid).\n   - _optional_\n\n**Returns**\n\nA [listener id](#eventlistenerid), which can be used to remove this specific listener. Unless manually provided via arguments this will be whatever the `emitter.getId` method spits out, and by default it spits out symbols which are guaranteed to be always unique.\n\n**Examples**\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter();\n\nconst a = () =\u003e console.log('a');\nconst b = () =\u003e console.log('b');\n\n// Bind a and b listeners to \"test\" event. Here we don't provide the third\n// argument (listener id) so it is created automatically and returned by the\n// .on() method.\nconst id1 = emitter.on('test', a);\nconst id2 = emitter.on('test', b);\n\n// Here we bind a and b listeners again to \"test\" event, but we provide the\n// listener id manually.\nconst id3 = emitter.on('test', a, 'foo');\nconst id4 = emitter.on('test', b, 'bar');\nid3 === 'foo'; // =\u003e true\nid4 === 'bar'; // =\u003e true\n\nemitter.emit('test');\n// a\n// b\n// a\n// b\n\nemitter.off('test', id2);\nemitter.emit('test');\n// a\n// a\n// b\n\nemitter.off('test', id1);\nemitter.emit('test');\n// a\n// b\n```\n\n### emitter.once()\n\nAdd a one-off listener to an event.\n\n**Syntax**\n\n```\nemitter.once( eventName, listener, [ listenerId ] )\n```\n\n**Parameters**\n\n1. **eventName**\n   - The name of the event you want to add a listener to.\n   - Accepts: [`EventName`](#eventname).\n2. **listener**\n   - A listener function that will be called when the event is emitted.\n   - Accepts: [`EventListener`](#eventlistener).\n3. **listenerId**\n   - The id for the listener. If not provided, the id will be generated by the `emitter.getId` function.\n   - Accepts: [`EventListenerId`](#eventlistenerid).\n   - _optional_\n\n**Returns**\n\nA [listener id](#eventlistenerid), which can be used to remove this specific listener. Unless manually provided via arguments this will be whatever the `emitter.getId` method spits out, and by default it spits out symbols which are guaranteed to be always unique.\n\n**Examples**\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter();\nconst a = () =\u003e console.log('a');\nconst b = () =\u003e console.log('b');\n\nemitter.on('test', a);\nemitter.once('test', b);\n\nemitter.emit('test');\n// a\n// b\n\nemitter.emit('test');\n// a\n```\n\n### emitter.off()\n\nRemove an event listener or multiple event listeners. If no _listenerId_ is provided all listeners for the specified event will be removed. If no _eventName_ is provided all listeners from the emitter will be removed.\n\n**Syntax**\n\n```\nemitter.off( [ eventName ], [ listenerId ] );\n```\n\n**Parameters**\n\n1. **eventName**\n   - The name of the event you want to remove listeners from.\n   - Accepts: [`EventName`](#eventname).\n   - _optional_\n2. **listenerId**\n   - The id of the listener you want to remove.\n   - Accepts: [`EventListenerId`](#eventlistenerid).\n   - _optional_\n\n**Examples**\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter();\n\nconst a = () =\u003e console.log('a');\nconst b = () =\u003e console.log('b');\n\nconst id1 = emitter.on('test', a);\nconst id2 = emitter.on('test', b);\nconst id3 = emitter.on('test', a);\nconst id4 = emitter.on('test', b);\n\n// Remove specific listener by id.\nemitter.off('test', id2);\n\n// Remove all listeners from an event.\nemitter.off('test');\n\n// Remove all listeners from the emitter.\nemitter.off();\n```\n\n### emitter.emit()\n\nEmit events.\n\n**Syntax**\n\n```\nemitter.emit( eventName, [ ...args ] )\n```\n\n**Parameters**\n\n1. **eventName**\n   - The name of the event you want to emit.\n   - Accepts: [`EventName`](#eventname).\n2. **...args**\n   - The arguments which will be provided to the listeners when called.\n   - Accepts: `any`.\n   - Optional.\n\n**Examples**\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter();\n\nemitter.on('test', (...args) =\u003e console.log(args.join('-')));\n\n// Provide arguments to the event's listeners.\nemitter.emit('test', 1, 2, 3, 'a', 'b', 'c');\n// '1-2-3-a-b-c'\n```\n\n### emitter.listenerCount()\n\nReturns the listener count for an event if _eventName_ is provided. Otherwise returns the listener count for the whole emitter.\n\n**Syntax**\n\n```\nemitter.listenerCount( [ eventName ] )\n```\n\n**Parameters**\n\n1. **eventName**\n   - The name of the event you want to get the listener count for.\n   - Accepts: [`EventName`](#eventname).\n   - Optional.\n\n**Examples**\n\n```ts\nimport { Emitter } from 'eventti';\n\nconst emitter = new Emitter();\n\nemitter.on('a', () =\u003e {});\nemitter.on('b', () =\u003e {});\nemitter.on('b', () =\u003e {});\nemitter.on('c', () =\u003e {});\nemitter.on('c', () =\u003e {});\nemitter.on('c', () =\u003e {});\n\nemitter.listenerCount('a'); // 1\nemitter.listenerCount('b'); // 2\nemitter.listenerCount('c'); // 3\nemitter.listenerCount(); // 6\n```\n\n### Types\n\nHere's a list of all the types that you can import from `eventti`.\n\n```ts\nimport {\n  EventName,\n  EventListener,\n  EventListenerId,\n  Events,\n  EmitterDedupe,\n  EmitterOptions,\n} from 'eventti';\n```\n\n#### EventName\n\n```ts\ntype EventName = string | number | symbol;\n```\n\n#### EventListener\n\n```ts\ntype EventListener = (...data: any) =\u003e any;\n```\n\n#### Events\n\n```ts\ntype Events = Record\u003cstring | number | symbol, EventListener\u003e;\n```\n\n#### EventListenerId\n\n```ts\ntype EventListenerId = null | string | number | symbol | bigint | Function | Object;\n```\n\n#### EmitterDedupe\n\n```ts\ntype EmitterDedupe = 'add' | 'update' | 'ignore' | 'throw';\n```\n\n#### EmitterOptions\n\n```ts\ntype EmitterOptions = {\n  dedupe?: EmitterDedupe;\n  getId?: (listener: EventListener) =\u003e EventListenerId;\n};\n```\n\n## License\n\nCopyright © 2022-2025, Niklas Rämö (inramo@gmail.com). Licensed under the [MIT license](./LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fniklasramo%2Feventti","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fniklasramo%2Feventti","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fniklasramo%2Feventti/lists"}