Adding IDE code completion support for custom event emitter functions?
RickKukiela opened this issue · comments
I've been using this package for an event heavy project I'm working on and its been great to work with. Thanks :) - I am struggling to figure out a way to add my own emitter definitions so my IDE understands when I start typing the .emit()
code it can help me by showing me what parameters it will accept. Based on what I have seen other people doing with other libraries and what I gathered from looking at your source code this is my best attempt:
"use strict";
import {EventEmitter, EventListener, EventNames} from "eventemitter3";
export interface CEvents {
'before-request': any[]; // based on your comments in the source code on extending interface EventTypes
}
export declare interface CEventEmitter {
on<T extends EventNames<CEvents>>(
event: T,
fn: EventListener<CEvents, T>,
context?: any
): this;
// emit
}
export class CEventEmitter extends EventEmitter {
constructor() {
super();
}
}
As you can see I haven't even been able to implement the on*
method correctly since I still have the emit*
section commented out. This is as close as I can get it and I pretty much hit a wall. My compiler gives me the following complaint with this setup (when hovering CEventEmitter
of export declare interface CEventEmitter
):
TS2430: Interface 'CEventEmitter' incorrectly extends interface 'EventEmitter<string | symbol, any>'.
Types of property 'on' are incompatible.
Type '<T extends "before-request">(event: T, fn: (...args: ArgumentMap<EventTypes>[Extract<T, "before-request">]) => void, context?: any) => this' is not assignable to type '<T extends string | symbol>(event: T, fn: (...args: any[]) => void, context?: any) => this'.
Types of parameters 'event' and 'event' are incompatible.
Type 'T' is not assignable to type '"before-request"'.
Type 'string | symbol' is not assignable to type '"before-request"'.
Type 'string' is not assignable to type '"before-request"'.
I hope someone out there can see what I'm missing and help me out... I'm sorry if this is the wrong place for this post. I'm not sure where else I would put it besides stack overflow. I figured I would try here since this is kind niche. I'm not new to JavaScript but this is my first stab at a real TypeScript project so I'm trying to understand and learn how this works. Thank you in advance.
hello
not a maintainer or anything, just put some thoughts regarding your task and this is what I ended up with:
import { EventEmitter } from 'eventemitter3';
interface Events {
event1: { payload1: string };
event2: { payload2: number };
}
class TypedEmitter< Events = Record< string, unknown > > {
__ee = new EventEmitter();
emit< K extends keyof Events >(type: K, payload: Events[K]) {
this.__ee.emit(String(type), payload);
}
on< K extends keyof Events >(type: K, handler: (payload: Events[K]) => unknown) {
this.__ee.on(String(type), handler);
}
}
const typedEmitterInstance = new TypedEmitter<Events>();
typedEmitterInstance.emit('event1', { payload1: '' });
typedEmitterInstance.on('event2', p => console.log(p.payload2));
IMHO, gives pretty decent autocomplete experience
My setup is
- VSCode 1.74.2
- eventemitter3@4.0.7
- typescript v 4.9.4
Hi, guys.
I had the same problem with IDE support. So I found another solution - C#-like events.
It greatly improved the navigation and the editor hints.
I use VSCode with @js-doc, no Typescript.
There is an example how I define events:
class Kitty {
/** @readonly */
emitter = new EventEmitter();
/**
* Event with 2 args: string, integer.
* @type {TypedEvent<[string, integer]>}
* @readonly
*/
eventSay = new TypedEvent(this.emitter, "say");
/**
* @param {string} word
* @param {integer} cnt
*/
say(word, cnt) {
this.eventSay.safeEmit(word, cnt); // editor shows the correct signature
}
}
const kitty = new Kitty();
kitty.eventSay.on((w, c) => { throw new Error('unhandled') }); // it will be ignored due to safeEmit
// editor shows the correct signature
kitty.eventSay.once((w, c) => console.log(`once say ${w}:${typeof(w)} ${c}:${typeof(c)}`));
kitty.eventSay.on((w, c) => console.log(`on say ${w}:${typeof(w)} ${c}:${typeof(c)}`));
kitty.say("meow", 3);
Repo link: https://github.com/dgolovin-dev/eventemitter3-typedevent
This is a bit old, but for others, here's how I solved it with TypeScript.
In my case I wanted a custom class that is itself just an emitter, so I do things like this.emit("...", ...)
in methods.
import { EventEmitter } from "eventemitter3";
// The types of the arguments you'll pass
interface argOne {
foo: string;
}
interface argTwo {
bar: number;
}
// This will contain all possible events and the args those events will pass
interface MyCustomEvents {
"my.event": [argOne, argTwo];
}
// A new event emitter that uses the custom events
class TypedEmitter extends EventEmitter<MyCustomEvents> {
constructor(props) {
super();
}
doThing(text: string) {
const argA = {foo: text};
const argB = {bar: 1};
this.emit("my.event", argA, argB);
}
}
const test = new TypedEmitter();
test.on("my.event", (argOne, argTwo) => {
// argOne is of type argOne
// argTwo is of type argTwo
});
// My IDE will yell at me in these, for the respective reasons:
// no such event
test.on("doesnt.exist", () => {})
// that's not an operation I can perform on the type argOne is
test.on("my.event", (argOne) => {
argOne++;
});
// you don't receive that many args
test.on("my.event", (argOne, argTwo, argThree) => {
});
// It will also yell at me for the same reasons when using .emit()
- WebStorm 2023.3.4
- TypeScript 5.2.2
- eventemitter3@5.0.1