如何用带联合类型参数的函数包装重载的addListener函数
问题描述
现有外部库包含4个重载的addListener函数(无法修改库代码):
addListener(eventName: 'change', listenerFunc: ChangeListener): Promise<PluginListenerHandle> & PluginListenerHandle; addListener(eventName: 'open', listenerFunc: OpenListener): Promise<PluginListenerHandle> & PluginListenerHandle; addListener(eventName: 'restore', listenerFunc: RestoreListener): Promise<PluginListenerHandle> & PluginListenerHandle; addListener(eventName: 'back', listenerFunc: BackListener): Promise<PluginListenerHandle> & PluginListenerHandle;
希望封装一个独立函数,在回调外层包裹Angular Zone逻辑:
wrapListenerCallback<T extends Listener>(eventName: T, callableFunction: ListenerCallback) { return addListener(eventName, (data) => { this.ngZone.run(() => { // 无关逻辑 callableFunction(data) }) }) }
当前实现出现两个TypeScript错误:
Argument of type 'Listener' is not assignable to parameter of type '"back"'. Type '"change"' is not assignable to type '"back"'.ts(2769)
若将addListener第一个参数硬改为'open',则出现:
TS2345: Argument of type 'OpenEvent' is not assignable to parameter of type 'ChangeEvent & OpenEvent & RestoreEvent & BackEvent'.
相关类型定义
外部库类型
export interface ChangeEvent { argument: boolean; } export interface OpenEvent { argument: boolean; argument2?: any; } export interface RestoreEvent { argument: string; argument2: string; } export interface BackEvent { argument: string; } export declare type ChangeListener = (event: ChangeEvent) => void; export declare type OpenListener = (event: OpenEvent) => void; export declare type RestoreListener = (event: RestoreEvent) => void; export declare type BackListener = (event: BackEvent) => void; export interface PluginListenerHandle { remove: () => Promise<void>; }
自有项目类型
export type Listener = 'change' | 'open' | 'restore' | 'back' export type ListenerCallback = ChangeListener | OpenListener | RestoreListener | BackListener
需求:实现wrapListenerCallback,传入Listener类型参数调用addListener时,回调类型自动匹配对应事件的特定类型,而非联合类型。
解决方案
核心问题是原泛型未建立事件名与对应回调类型的关联,导致TypeScript无法推断正确的类型约束。需通过映射类型明确事件与回调的对应关系,再用泛型绑定两者。
步骤1:创建事件-回调映射类型
定义映射类型,将每个事件名与对应的回调类型一一绑定:
type EventListenerMap = { change: ChangeListener; open: OpenListener; restore: RestoreListener; back: BackListener; };
步骤2:重构封装函数
利用泛型T约束为keyof EventListenerMap,让事件名和回调类型强关联:
wrapListenerCallback<T extends keyof EventListenerMap>( eventName: T, callableFunction: EventListenerMap[T] ) { return addListener(eventName, (data) => { this.ngZone.run(() => { callableFunction(data); }); }); }
关键说明
- 用
keyof EventListenerMap替代原Listener类型,确保泛型T只能是映射中存在的合法事件名。 EventListenerMap[T]会根据传入的eventName自动推导对应的回调类型(比如传入'change'时,回调类型自动匹配ChangeListener)。- 此时TypeScript能正确匹配
addListener的重载,既解决类型不兼容错误,又让回调参数类型自动对应到事件的具体Event类型。
内容的提问来源于stack exchange,提问作者Sander Schnydrig
相关产品推荐
相关产品推荐

