TypeScript Event Emitter类型问题求助:如何修复代码类型报错?
原代码
export type IEventMap = { "mount": () => void; "mounted": () => void; "unmount": () => void; "unmounted": () => void; "test1": (stringArg: string) => void; "test2": (numberArg: number) => void; }; export type IEventNames = keyof IEventMap; export type IEventFn<K extends IEventNames> = IEventMap[K]; export type IEventListeners = { [K in IEventNames]?: Set<IEventFn<K>> }; export default class Events { private readonly listeners: IEventListeners = {}; public on <K extends IEventNames> (eventName: K, callback: IEventFn<K>) { if (!this.listeners[eventName]) { this.listeners[eventName] = new Set(); } this.listeners[eventName].add(callback); } public off <K extends IEventNames> (eventName: K, callback: IEventFn<K>) { if (this.listeners[eventName]) { this.listeners[eventName].delete(callback); } } public emit <K extends IEventNames> (eventName: K, ...args: Parameters<IEventFn<K>>) { if (this.listeners[eventName]) { this.listeners[eventName].forEach(callback => callback(...args)); } } } // test const events = new Events(); events.on("mount", () => { console.log("mount"); }); events.on("test1", arg => { console.log(arg); }); events.on("test2", arg => { console.log(arg); }); events.emit("mount"); events.emit("test1", "test string"); events.emit("test2", 1);
报错信息
类型'Set<IEventFn
>'无法分配给类型'IEventListeners[K]'。类型'Set<IEventFn >'无法分配给类型'Set<() => void> & Set<() => void> & Set<() => void> & Set<() => void> & Set<(stringArg: string) => void> & Set<(numberArg: number) => void>'。类型'Set<IEventFn >'无法分配给类型'Set<() => void>'。类型'IEventFn '无法分配给类型'() => void'。类型'(() => void) | (() => void) | (() => void) | (() => void) | ((stringArg: string) => void) | ((numberArg: number) => void)'无法分配给类型'() => void'。类型'(stringArg: string) => void'无法分配给类型'() => void'。
对象可能为'undefined'。对象可能为'undefined'。对象可能为'undefined'。
扩展参数必须具有元组类型或传递给剩余参数。
修复后的代码
export type IEventMap = { "mount": () => void; "mounted": () => void; "unmount": () => void; "unmounted": () => void; "test1": (stringArg: string) => void; "test2": (numberArg: number) => void; }; export type IEventNames = keyof IEventMap; export type IEventFn<K extends IEventNames> = IEventMap[K]; export type IEventListeners = { [K in IEventNames]?: Set<IEventFn<K>> }; export default class Events { private readonly listeners: IEventListeners = {}; public on <K extends IEventNames> (eventName: K, callback: IEventFn<K>) { if (!this.listeners[eventName]) { this.listeners[eventName] = new Set<IEventFn<K>>(); } this.listeners[eventName]!.add(callback); } public off <K extends IEventNames> (eventName: K, callback: IEventFn<K>) { this.listeners[eventName]?.delete(callback); } public emit <K extends IEventNames> (eventName: K, ...args: Parameters<IEventFn<K>>) { const eventListeners = this.listeners[eventName]; if (eventListeners) { eventListeners.forEach((callback: IEventFn<K>) => { callback(...args); }); } } } // test 代码无需修改 const events = new Events(); events.on("mount", () => { console.log("mount"); }); events.on("test1", arg => { console.log(arg); }); events.on("test2", arg => { console.log(arg); }); events.emit("mount"); events.emit("test1", "test string"); events.emit("test2", 1);
修复说明
Set类型不匹配:
原代码new Set()未指定泛型,TypeScript会推断为包含所有事件函数的联合类型集合,与当前事件K的特定函数类型不匹配。显式声明new Set<IEventFn<K>>(),让集合类型与当前事件的回调类型严格对齐。对象可能为undefined:
on方法中,用非空断言!告知TypeScript,经过判断后listeners[eventName]一定存在;off方法改用可选链?.,直接调用delete,不存在则自动短路,无需额外判断。
扩展参数类型报错:
在emit的forEach中显式指定回调类型为IEventFn<K>,确保TypeScript能正确推断args与回调参数的兼容性,解决扩展参数的类型不匹配问题。
内容的提问来源于stack exchange,提问作者Alex

