将方法签名限制为仅允许事件映射中定义的类型的有效负载

Restrict method signature to only allow payload of type defined in event map

我正在开发一个事件 (pub/sub) 系统,我需要预先定义事件和有效负载。无法将 publish 方法限制为 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

这是打字稿推断出的签名:

(method) SomeClass<EVENT_SIGNATURES, EVENTS>.publish(eventName: EVENTS, payload: {
    messageId: string;
} | {
    age: number;
}): void

而我需要将有效负载限制为 EVENTNAME。

这里还有一个沙盒:https://codesandbox.io/s/bold-joliot-1bi9m?file=/src/index.ts:591-693

提前致谢!

尝试使 publish 通用,记住传入的 eventName

class SomeClass<EVENT_MAP> {
    publish<EVENTNAME extends keyof EVENT_MAP>(eventName: EVENTNAME, payload: EVENT_MAP[EVENTNAME]) {}
}