如何实现保留参数类型、替换返回值类型的TypeScript wrap函数?
问题描述
希望创建一个wrap()函数,实现如下效果:
const fn = (x: string) => 'some string'; interface CustomObject { id: number; title: string; } const wrapped = wrap<CustomObject>(fn); // wrapped的类型必须为: (x: string) => CustomObject
尝试过程中遇到两个问题:
问题一
以下代码无法正常工作,因为指定第一个泛型参数时,必须同时指定所有其他无默认值的泛型参数:
function wrap <Schema, Args extends any[]> (fn: (...args: Args) => any) { return (...args: Args): Schema => fn(...args); } const myFunction = (bar: string) => 'some string'; const myFunctionWrapper = wrap<{ id: number }>(myFunction); // -----------------------------------------^: 期望2个类型参数,但仅传入1个。
问题二
如果为第二个泛型参数设置默认类型,则会丢失原函数fn的参数类型:
function wrap <Schema, Args extends any[] = any[]> (fn: (...args: Args) => any) { return (...args: Args): Schema => fn(...args); } const myFunction = (bar: string) => 'some string'; const myFunctionWrapper = wrap<{ id: number }>(myFunction); // myFunctionWrapper的类型为: (...args: any[]) => { id: number },而非(bar: string) => { id: number }
疑问:
- 我哪里出错了?
- 在TypeScript中是否可以实现该需求?
问题解答
出错原因
- 第一种写法中,
Schema和Args都是无默认值的泛型参数,TypeScript要求必须显式传入所有此类参数,只传Schema会触发类型错误。 - 第二种写法给
Args设置了any[]默认值,当你显式指定Schema时,TypeScript会直接使用Args的默认值,跳过对原函数参数类型的推导,导致参数类型丢失。
可以实现需求,推荐两种方案
方案一:柯里化拆分调用(最直观)
通过柯里化把函数拆分为两次调用:第一次显式指定返回值类型Schema,第二次传入原函数,此时TypeScript会自动推导原函数的参数类型:
function wrap<Schema>() { return <F extends (...args: any[]) => any>(fn: F): (...args: Parameters<F>) => Schema => { return (...args) => fn(...args) as Schema; }; } // 使用示例 const fn = (x: string) => 'some string'; interface CustomObject { id: number; title: string; } const wrapped = wrap<CustomObject>()(fn); // wrapped类型为: (x: string) => CustomObject
方案二:利用TS 4.7+的泛型部分指定(更简洁)
如果你的项目使用TypeScript 4.7及以上版本,可以调整泛型参数逻辑,实现直接调用wrap<Schema>(fn)的效果:
function wrap<Schema, F extends (...args: any[]) => any>( fn: F ): (...args: Parameters<F>) => Schema { return (...args) => fn(...args) as Schema; } // 使用示例 const wrapped = wrap<CustomObject>(fn); // wrapped类型为: (x: string) => CustomObject
内容的提问来源于stack exchange,提问作者localusercamp
相关产品推荐
相关产品推荐

