TypeScript中根据入参函数同步/异步特性返回类型安全值的实现问题
解决方案
核心思路
- 用TS内置的
Awaited工具类型简化异步返回值的提取,无需自定义SyncType - 实现正确的
isAsync类型守卫,运行时用函数构造函数名判断是否为async函数 - 由于TS目前无法自动推导函数返回的条件类型匹配性,增加类型断言绕过校验
- 修复原代码中
MyClasss的拼写错误,修正测试用例中不合理的await merge(...)写法(merge返回的是函数,需调用后再await)
完整实现代码
class MyClass<T> { constructor(public data: T) {} } /** * 异步函数类型守卫 * @param func 待判断的函数 */ function isAsync<P extends any[], R>(func: (...args: P) => R): func is (...args: P) => Promise<Awaited<R>> { return func.constructor.name === 'AsyncFunction'; // 若需兼容非async声明但返回Promise的函数,可在业务场景允许的前提下补充运行时判断逻辑 } /** * 将指定函数的返回值包装为MyClass实例 * @param func 待包装的原函数 */ function merge<P extends any[], R>(func: (...args: P) => R) : (...args: P) => R extends Promise<infer A> ? Promise<MyClass<A>> : MyClass<R> { if (isAsync(func)) { return (async (...args: P) => { const value = await func(...args); return new MyClass(value); // 增加类型断言匹配返回的条件类型 }) as ReturnType<typeof merge<P, R>>; } return ((...args: P) => { const value = func(...args); return new MyClass(value); }) as ReturnType<typeof merge<P, R>>; }
测试验证
(async() => { // 异步函数测试 const asyncMergeFn = merge(async (value: string) => value === 'test' ? 1 : 0); // asyncMergeFn类型:(value: string) => Promise<MyClass<number>> const asyncRes = await asyncMergeFn('test'); // asyncRes类型:MyClass<number>,值为MyClass { data: 1 } // 同步函数测试 const syncMergeFn = merge((value: string) => value === 'test' ? 1 : 0); // syncMergeFn类型:(value: string) => MyClass<number> const syncRes = syncMergeFn('test'); // syncRes类型:MyClass<number>,值为MyClass { data: 1 } // 直接调用测试 const directAsyncRes = await merge(async (value: string) => value === 'test' ? 1 : 0)('test'); // directAsyncRes类型:MyClass<number> const directSyncRes = merge((value: string) => value === 'test' ? 1 : 0)('test'); // directSyncRes类型:MyClass<number> })();
扩展说明
如果你的业务场景需要支持所有返回Promise的非async声明函数,只需修改isAsync的运行时判断逻辑即可,TS的类型系统已经能通过泛型推导识别返回Promise的函数,类型守卫可以正常工作。
内容的提问来源于stack exchange,提问作者Decisi0n
相关产品推荐
相关产品推荐

