Open leumasme opened 3 years ago
Hi @leumasme!
Have you tried something like this yet?
import { EventEmitter } from "events"
import TypedEmitter from "typed-emitter"
type MessageEvents = {
`foo:(${string},${string},${string})`: (payload: number) => void
}
const emitter = new EventEmitter() as TypedEmitter<MessageEvents>
emitter.emit("foo:(1,2,3)", 1)
Unfortunately I could not easily test that and share it, as codesandbox doesn't seem to do plain basic node + TypeScript sandboxes with latest TypeScript versions. Maybe you have an idea where to find something like that?
Hey @andywer
I tried using a template literal as you suggested, and this does not work.
VSCode complains about Property or signature expected.ts(1131)
I believe that this wont be possible without some explicit support from the library.
Alternatively, another Typed Emitter class that allows string as a fallback type so that explicitly declared event names will be autocompleted, but undeclared ones will still be valid might be an idea?
Honestly not sure as i'm not great at typescript myself, just fan-making some types for an existing project.
You definitely have to use a type alias as I did above, not an interface: type MessageEvents = { /* ... */ }
wont be possible without some explicit support from the library
The library supports strict event names. Actually that's more or less the library's whole purpose… 😅 We just have to figure out how to feed that information into TypeScript in a way that it is able to digest it 😉
You definitely have to use a type alias as I did above, not an interface
Whoops, my bad.
Same TS warning though
Sorry if I'm being stupid, not great at typescript as said <-<
I forgot something, too: The computed property name must be written in brackets ([`foo:…`]: …
)
Anyhow, TypeScript will still fail to compile and tell us that this is not supported…
So it's not possible right now. However the limitation is rooted in the way event types are passed to typed-emitter
at the moment as the feature that would be needed is supported by TypeScript, but just not for property names.
I will have to go think if I can come up with an alternative API that allows for template literal typed event names.
I think I've got something… See here.
I tried to seize the opportunity to turn the syntax into something that's hopefully easier to read than the old syntax, too:
type MessageEvents =
| EmitterEvent<`foo:${string}`, (size: number, strict: boolean) => void>
| EmitterEvent<`bar:${string}`, (force: boolean) => void>
const messageEmitter = new EventEmitter() as TypedEmitter<MessageEvents>
messageEmitter.emit("foo:1", 2, false)
ping @leumasme
@andywer thanks for hackin on this! i find your proposed solution interesting but after forking your example, i found that i was able to emit the foo:1
event with the arguments of the handler function for bar:1
. see this for reference of below:
messageEmitter.emit("foo:1", false) // this should not work...
personally, i like the current syntax and would prefer your other solution of putting the dynamic event name in brackets as the key of an interface. like: [`foo:${string}`]: () => void
do you think it'd be possible to make this way work?
Hey there! Is there any way to type events with variable event names? Specifically, i am trying to type https://github.com/PrismarineJS/prismarine-world/blob/55a6f78b4802519b7bee7ace20daa7e956e6ca7f/src/worldsync.js#L69 The World Class emits events called
blockUpdate:${position}
, which stringifies toblockUpdate:(123, 123, 123)
. Is typing this kind of event possible with typed-emitter? I know that such complex string type matching is possible in Typescript, see Typescript DFA, but is there any way to do this while using typed-emitter? Thanks for your time!