Proxy包装后类方法返回类型丢失的TypeScript类型推断问题
TypeScript错误捕获包装器的类型推断修复方案
问题背景
尝试实现一个错误捕获包装器,让类方法调用后返回[result | undefined, error | undefined]元组,但TypeScript的类型推断未按预期工作。原实现代码如下:
const _catch = async <T>( cb: () => Promise<T> | T ): Promise<[T | undefined, string | undefined]> => { try { const result = await cb(); return [result, undefined]; } catch (error) { if (error instanceof Error) throw error; return [undefined, error as string]; } }; const wrapCatch = <T extends object>(instance: T, excludes: string[] = []) => new Proxy(instance, { get(target, prop, receiver) { const original = Reflect.get(target, prop, receiver); if (typeof original !== 'function' || excludes.includes(prop as string)) return original; return async (...args: any[]) => { return _catch(() => (original as (...args: any[]) => any).apply(target, args)); }; }, }); function something(n : any){ return "hello" } class Foo { constructor() { return wrapCatch(this); } async boo(params: string) { const result = something(params); // 也支持调用异步函数 // const result = await something(params) if (!result) throw "custom error"; return result; } }
调用const result = new Foo().boo("any")时,预期类型为Promise<[Awaited<ReturnType<typeof something>> | undefined, string | undefined]>,但实际推断类型为Promise<Awaited<ReturnType<typeof something>>>,只能手动指定类型解决,需要修复类型推断问题。
问题原因
原wrapCatch函数的泛型仅约束输入对象类型,但没有对输出的Proxy对象类型进行精确映射——TypeScript无法自动推断出Proxy拦截后方法返回值的类型变化,需要手动定义类型转换逻辑,将原对象的方法类型转换为包装后的元组Promise类型。
修复方案
通过映射类型重新定义wrapCatch的返回类型,将原对象中的每个方法转换为返回Promise<[Awaited<ReturnType<Fn>> | undefined, string | undefined]>的异步函数,非方法属性保持原类型:
// 定义包装后的方法类型 type WrappedMethod<Fn extends (...args: any[]) => any> = (...args: Parameters<Fn>) => Promise<[Awaited<ReturnType<Fn>> | undefined, string | undefined]>; // 定义包装后的对象类型:方法替换为WrappedMethod,非方法保持原类型 type WrappedObject<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? WrappedMethod<T[K]> : T[K]; }; const _catch = async <T>( cb: () => Promise<T> | T ): Promise<[T | undefined, string | undefined]> => { try { const result = await cb(); return [result, undefined]; } catch (error) { if (error instanceof Error) throw error; return [undefined, error as string]; } }; const wrapCatch = <T extends object>(instance: T, excludes: string[] = []): WrappedObject<T> => new Proxy(instance, { get(target, prop, receiver) { const original = Reflect.get(target, prop, receiver); const propStr = prop as string; if (typeof original !== 'function' || excludes.includes(propStr)) return original; // 约束original为函数类型 const fn = original as (...args: any[]) => any; return async (...args: Parameters<typeof fn>) => { return _catch(() => fn.apply(target, args)); } as WrappedMethod<typeof fn>; }, }) as WrappedObject<T>; function something(n: any) { return "hello"; } class Foo { constructor() { return wrapCatch(this); } async boo(params: string) { const result = something(params); // 支持异步调用 // const result = await something(params); if (!result) throw "custom error"; return result; } } // 现在类型推断正确 const result = new Foo().boo("any"); // result的类型为:Promise<[string | undefined, string | undefined]>
关键改进点
- WrappedMethod类型:将原方法的参数和返回值转换为包装后的异步元组类型,使用
Awaited处理原方法的Promise返回值。 - WrappedObject类型:通过映射类型遍历原对象的所有属性,仅替换方法类型,非方法属性保持原样。
- wrapCatch返回类型约束:明确指定返回
WrappedObject<T>,让TypeScript正确推断包装后对象的方法类型。
内容的提问来源于stack exchange,提问作者최도영
相关产品推荐
相关产品推荐

