-
Notifications
You must be signed in to change notification settings - Fork 1
typed-emitter
compatibility
#4
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Hi Huan! The behavior is actually expected and is caused because of the way types are written keeping a limitation of TypeScript in mind. The limitation is that inference breaks when you have generics involved in a function (as TypedEventEmitter uses them on all the methods), an example (demo): type Identity = <T>(x: T) => T;
type Test = Identity extends (x: string) => infer T ? T : never;
// Test is expected to be `string` but is `unknown` Now I can solve this but it would require having typed-emitter as a dependency, but that still would only solve the problem only for people who use typed-emitter not for others who have emitters with generics. So unfortunately supporting typed-emitter out of the box magically is a non-goal for rxjs-from-emitter. But that being said I can still solve you're problem in a much much simpler way. I believe you want fromEvent to work with TypedEventEmitter right? Here's the solution: // utils/from-typed-emitter.ts
import { EventEmitter } from 'events';
import { fromEvent, Observable } from "rxjs";
import TypedEventEmitter from "typed-emitter";
type FromTypedEvent =
< Emitter extends TypedEventEmitter<any>
, EventName extends keyof Events
, Events = Emitter extends TypedEventEmitter<infer T> ? T : never
>(emitter: Emitter, event: EventName ) =>
Observable<ObservedValue<Events[EventName] extends (...args: infer A) => any ? A : never>>
type ObservedValue<A extends unknown[]> =
A["length"] extends 0 ? void :
A["length"] extends 1 ? A[0] :
A
export const fromTypedEvent = fromEvent as FromTypedEvent; Now you can simply use it as you would use import { fromTypedEvent } from "./utils/from-typed-emitter";
import TypedEventEmitter from "typed-emitter";
interface Events {
foo: (n: number) => void
bar: (s: string, x: number) => void,
baz: () => void
}
const TypedEmitter = EventEmitter as new () => TypedEventEmitter<Events>
const emitter = new TypedEmitter()
fromTypedEvent("", "") // error
fromTypedEvent(emitter, "") // error
fromTypedEvent(emitter, "foo") // Observable<number>
fromTypedEvent(emitter, "bar") // Observable<[string, number]>
fromTypedEvent(emitter, "baz") // Observable<void> So if you're using typed-emitter, you actully don't need rxjs-from-emitter because it's made for emitters that are actually typed in a hardcoded way like this. One day when we no longer have that TypeScript limitation rxjs-from-emitter will be able to support typed-emitter without doing anything special. I hope this helps! I'm closing this as I said it's a non-goal. Thanks for the interest tho! |
fromEmitter(typedEmitter).event('foo')
returns Observable<unknown>
typed-emitter
compatibility
Opened #5 for tracking any improvements in this space. Likely there will be none till we don't have that TypeScript limitation resolved. |
Hi Devanshj, You are so kind for responding to my question with so detailed analytic explanation! I understand the problem is caused by a limitation of the current version of TypeScript now, and thank you very much for you great solution code, it works like a charm! Cheers, and have a great day! |
Oops, I found that our solution code does not work when we do a
import { EventEmitter } from 'events'
import { fromTypedEvent } from './from-typed-event'
import TypedEventEmitter from 'typed-emitter'
interface Events {
foo: (n: number) => void
}
const TypedEmitter = EventEmitter as new () => TypedEventEmitter<Events>
// We declare a Test class
// |
// v
class Test extends TypedEmitter {}
const emitter = new Test()
fromTypedEvent(emitter, '')
// Oops! No error
fromTypedEvent(emitter, 'foo')
// Oops! Observable<void> I'm trying to see how to fix it now... |
You're welcome! I'll look into helping you with a solution in some time |
I can't really figure out why it's not exactly working right now, but you can use this workaround: import { EventEmitter } from 'events';
import { fromEvent, Observable } from "rxjs";
import OriginalTypedEventEmitter from "typed-emitter";
type TypedEventEmitter<T> =
& OriginalTypedEventEmitter<T>
& { __events: T } // <-- only change
type FromTypedEvent =
< Emitter extends TypedEventEmitter<any>
, EventName extends keyof Events
, Events = Emitter extends TypedEventEmitter<infer T> ? T : never
>(emitter: Emitter, event: EventName ) =>
Observable<ObservedValue<Events[EventName] extends (...args: infer A) => any ? A : never>>
type ObservedValue<A extends unknown[]> =
A["length"] extends 0 ? void :
A["length"] extends 1 ? A[0] :
A
export const fromTypedEvent = fromEvent as FromTypedEvent;
interface Events {
foo: (n: number) => void
}
const TypedEmitter = EventEmitter as any as new () => TypedEventEmitter<Events>
class Test extends TypedEmitter {}
const emitter = new Test()
fromTypedEvent(emitter, '') // error
fromTypedEvent(emitter, 'foo') // Observable<number> |
Thank you so much for your solution, it works like a charm. Appreciate it! |
You're welcome! |
Hi @devanshj ,
Thank you very much for your great module! I run into the same issue as you before today. (see: wechaty/redux#4)
After performed my google-fu, I found your issue ReactiveX/rxjs#4891, and here, where I believe it will be my final destination. ;^)
I'm using the TypedEmitter which is really nice to type my emitter classes, when I use
fromEmitter()
with thetypedEmitter
, I found that if we have more than one event, unfortunately we will get anunknown
typing with the returned Observable, which I believe it is a bug.Minimum Reproducible Code
If we remove
bar
in theEvents
interface, then the$
will be able to be inference right:const $: Observable<number>
I tried to dive into your code to fix it, however, the Typing code is more than I expected.
Would really appreciate it if we can get our
fromEmitter
works withtypedEmitter
, thank you for your time for my issue, and looking forward to seeing your reply.The text was updated successfully, but these errors were encountered: