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

如何用带联合类型参数的函数包装重载的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 13:36:37