如何在TypeScript中推断Capacitor插件多签名方法的参数类型?
实现Capacitor插件监听器的类型安全Observable转换
我在Angular项目中使用各类Capacitor插件,这些插件包含方法与监听器。以Keyboard插件为例,其addListener方法存在多个重载:
addListener(eventName: 'keyboardWillShow', listenerFunc: (info: KeyboardInfo) => void): Promise<PluginListenerHandle> & PluginListenerHandle; addListener(eventName: 'keyboardDidShow', listenerFunc: (info: KeyboardInfo) => void): Promise<PluginListenerHandle> & PluginListenerHandle; addListener(eventName: 'keyboardWillHide', listenerFunc: () => void): Promise<PluginListenerHandle> & PluginListenerHandle; addListener(eventName: 'keyboardDidHide', listenerFunc: () => void): Promise<PluginListenerHandle> & PluginListenerHandle;
我编写了fromCapacitorListener方法来封装这些监听器,希望实现以下效果:
fromCapacitorListener(Keyboard, 'keyboardWillShow')返回类型:Observable<KeyboardInfo>fromCapacitorListener(Keyboard, 'keyboardWillHide')返回类型:Observable<void>fromCapacitorListener(Keyboard, 'nonexisting')触发TypeScript报错
当前的类型定义过于宽泛,无法实现上述约束,需要为该方法添加精准的类型限制:第一个参数接受任意Capacitor插件实例,第二个参数仅允许传入该插件addListener重载中存在的事件名;同时根据事件名自动推断回调参数类型,进而确定Observable的泛型类型。
解决方案
通过TypeScript的条件类型和重载提取技巧,先定义辅助类型解析插件的addListener重载信息,再实现带完整类型约束的方法:
1. 定义辅助类型
// 提取插件addListener的所有重载类型 type ExtractAddListenerOverloads<T> = T extends { addListener: infer U } ? U : never; // 从单个addListener重载中提取事件名与对应回调参数类型 type ExtractEventMap<Overloads> = Overloads extends { (...args: [eventName: infer E, listenerFunc: (info: infer P) => void]): any; } ? { [K in E]: P } : never; // 合并多个重载的事件映射,处理多重重载场景 type MergeEventMaps<T> = T extends ((...args: any[]) => any)[] ? T extends [infer First, ...infer Rest] ? ExtractEventMap<First> & MergeEventMaps<Rest> : {} : ExtractEventMap<T>;
2. 实现带类型约束的核心方法
import { Observable } from 'rxjs'; import type { PluginListenerHandle } from '@capacitor/core'; export function fromCapacitorListener< T extends { addListener: any }, E extends keyof MergeEventMaps<ExtractAddListenerOverloads<T>> >( plugin: T, eventName: E ): Observable<MergeEventMaps<ExtractAddListenerOverloads<T>>[E]> { return new Observable((subscriber) => { // 调用插件的addListener注册监听器 const listenerHandle = plugin.addListener(eventName, (info) => { subscriber.next(info); }); // 清理逻辑:取消监听器订阅 return async () => { await (await listenerHandle).remove(); }; }); }
3. 验证效果
// 符合预期:返回Observable<KeyboardInfo> fromCapacitorListener(Keyboard, 'keyboardWillShow'); // 符合预期:返回Observable<void> fromCapacitorListener(Keyboard, 'keyboardWillHide'); // 触发TS错误:"nonexisting"不在允许的事件名列表中 fromCapacitorListener(Keyboard, 'nonexisting');
内容的提问来源于stack exchange,提问作者Fabien LH
相关产品推荐
相关产品推荐

