I am working on an event (pub/sub) system and I need to define the events and payloads upfront. Having trouble restricting the publish
method to EVENT, EVENT[PAYLOAD].
// This is the closest I got to, but it isn't working:
class SomeClass<EVENT_MAP, EVENTNAME extends keyof EVENT_MAP> {
publish(eventName: EVENTNAME, payload: EVENT_MAP[EVENTNAME]) {}
}
enum EVENTS {
HELLO,
WORLD
}
type EVENT_SIGNATURES = {
[EVENTS.HELLO]: {
messageId: string
},
[EVENTS.WORLD]: {
age: number
}
}
// This does not work as expected. What am I doing wrong?
const someClass = new SomeClass<EVENT_SIGNATURES, keyof EVENT_SIGNATURES>();
// Here's how I expect it work:
someClass.publish(EVENTS.HELLO, { messageId: 123 }) // => error. string expected
someClass.publish(EVENTS.HELLO, { messageId: "123", age: 15 }); // => error. age shouldn't be on here
someClass.publish(EVENTS.HELLO, { messageId: '123' }) // => good
someClass.publish(EVENTS.WORLD, { messageId: '123' }) // => error. messageId isn't on the type
someClass.publish(EVENTS.WORLD, { age: 123 }) // => good
// Here's what actually happens
someClass.publish(EVENTS.HELLO, { messageId: 123 }); // => error. string expected
someClass.publish(EVENTS.HELLO, { messageId: "123", age: 15 }); good
someClass.publish(EVENTS.HELLO, { messageId: "123" }); // => good
someClass.publish(EVENTS.WORLD, { messageId: "123" }); // => good
someClass.publish(EVENTS.WORLD, { age: 123 }); // => good
This is the signature that typescript infers:
(method) SomeClass<EVENT_SIGNATURES, EVENTS>.publish(eventName: EVENTS, payload: {
messageId: string;
} | {
age: number;
}): void
Whereas I need payload to be restricted to the EVENTNAME.
Here's a sandbox too: https://codesandbox.io/s/bold-joliot-1bi9m?file=/src/index.ts:591-693
Thanks in advance!
Try to make publish generic, to remember the eventName
that was passed in:
class SomeClass<EVENT_MAP> {
publish<EVENTNAME extends keyof EVENT_MAP>(eventName: EVENTNAME, payload: EVENT_MAP[EVENTNAME]) {}
}