I am currently working on a module that allows callers to subscribe to specific events. The caller provides the event name as the first argument when subscribing, and I want to be able to infer the callback signature from this argument.
In my implementation, I am using an RxJS I can't get this to compile successfully. The TS Playground I've linked to shows the correct result that I'm after on line:45, but the compiler is complaining about the Subjects type signature, and I can't seem to resolve it. What am I missing?Subject
for each supported event and creating a subscription to it every time myModule.subscribe(eventType)
is called. A simplified version of the implementation is shown below (you can also see it in action on import { Subject, Subscription } from "rxjs";
const EventNames = {
a: "a",
b: "b",
c: "c",
} as const;
type Payloads = {
[EventNames.a]: string;
[EventNames.b]: number;
[EventNames.c]: boolean;
};
type EventTypes = keyof typeof EventNames;
// all possible event objects
type Events<T> = T extends EventTypes ? {type: T, payload: Payloads[T]} : never;
// all possible callbacks
type Callback<T> = T extends EventTypes ? (event: Events<T>) => void : never;
// all possible subjects
type Subjects<T> = T extends EventTypes ? Subject<Events<T>> : never;
const collection = new Map<EventTypes, Subjects<EventTypes>>();
function fnWithConstraint<T extends EventTypes>(
name: T,
cb: Callback<T>
): Subscription | null {
const subject = collection.has(name)
? collection.get(name)
: new Subject<Events<T>>();
if (subject !== undefined) {
collection.set(name, subject);
/* ^ Type '{ type: "a"; payload: string; }' is not assignable
to type 'Events<T>'.
*/
const subscription = subject.subscribe(cb)
/* ^ This expression is not callable. Each member of the union
type '{ (observer?: Partial<Observer<Events<T>>> | undefined):
Subscription; (next: (value: Events<T>) => void): Subscription;
... 1 more ...' has signatures, but none of those signatures
are compatible with each other.
*/
return subscription;
}
return null;
}
fnWithConstraint("b", (event) => console.log(event));
// expect typeof event -> { type: "b"; payload: number; }