如何为Map添加类型约束,使其键值匹配指定接口的对应类型
解决方案
要让Map的键与对应值的handler类型严格匹配Events接口的定义,有两种常用方式:
方式一:使用对象替代Map(类型最精确)
如果业务场景允许用对象存储监听器,直接利用TypeScript的索引映射类型就能实现完美的类型约束:
interface Events { foo: (a: string, b: number) => void; bar: (a: boolean) => void; } // 定义每个事件对应的监听器项类型 type EventListenerItem<K extends keyof Events> = { handler: Events[K]; once: boolean; }; // 创建严格类型约束的监听器对象 const listeners: { [K in keyof Events]: EventListenerItem<K>[] } = { foo: [], bar: [] }; // 使用示例:类型完全匹配 listeners.foo.push({ handler: (str, num) => console.log(str, num), once: false }); listeners.bar.push({ handler: (bool) => console.log(bool), once: true });
这种方式下,访问listeners.foo时,TypeScript会自动推断出handler必须符合Events.foo的类型,完全不会出现类型不匹配的问题。
方式二:坚持使用Map(需通过泛型封装保证类型安全)
Map本身的类型系统无法直接做到键与值的一对一类型关联(因为Map的ValueType是全局统一的),但可以通过泛型函数封装Map的读写操作,来确保类型安全:
interface Events { foo: (a: string, b: number) => void; bar: (a: boolean) => void; } type EventListenerItem<K extends keyof Events> = { handler: Events[K]; once: boolean; }; // 定义Map的基础类型,值为所有监听器项的联合类型数组 const listeners = new Map<keyof Events, EventListenerItem<keyof Events>[]>(); // 泛型添加函数:确保添加的handler与事件键匹配 function addListener<K extends keyof Events>(key: K, listener: EventListenerItem<K>) { const existing = listeners.get(key) || []; existing.push(listener); listeners.set(key, existing); } // 泛型获取函数:确保返回的handler类型与事件键匹配 function getListeners<K extends keyof Events>(key: K): EventListenerItem<K>[] | undefined { return listeners.get(key) as EventListenerItem<K>[] | undefined; } // 使用示例:类型检查生效 addListener('foo', { handler: (str, num) => console.log(str, num), once: false }); addListener('bar', { handler: (bool) => console.log(bool), once: true });
通过泛型函数封装后,无论是添加还是获取监听器,TypeScript都会严格校验handler的类型是否与事件键对应。
内容的提问来源于stack exchange,提问作者SReject
相关产品推荐
相关产品推荐

