You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript Event Emitter类型问题求助:如何修复代码类型报错?

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);

修复说明

  1. Set类型不匹配:
    原代码new Set()未指定泛型,TypeScript会推断为包含所有事件函数的联合类型集合,与当前事件K的特定函数类型不匹配。显式声明new Set<IEventFn<K>>(),让集合类型与当前事件的回调类型严格对齐。

  2. 对象可能为undefined:

    • on方法中,用非空断言!告知TypeScript,经过判断后listeners[eventName]一定存在;
    • off方法改用可选链?.,直接调用delete,不存在则自动短路,无需额外判断。
  3. 扩展参数类型报错:
    在emit的forEach中显式指定回调类型为IEventFn<K>,确保TypeScript能正确推断args与回调参数的兼容性,解决扩展参数的类型不匹配问题。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:35:18