Typescript 是否可以包装函数并保留其类型?

Typescript 是否可以包装函数并保留其类型?,typescript,Typescript,我正在尝试创建一个通用包装函数,它将包装传递给它的任何函数 在最基本的情况下,包装器函数看起来像 function wrap<T extends Function>(fn: T) { return (...args) => { return fn(...args) }; } 现在wrappedFoo正在获取一种类型的(…args:any[])=>any 是否可以让wrappedFoo模拟其包装的函数类型?这是可能的,但如果希望能够传递不同类型和

我正在尝试创建一个通用包装函数,它将包装传递给它的任何函数

在最基本的情况下,包装器函数看起来像

function wrap<T extends Function>(fn: T) {
    return (...args) => {
        return fn(...args)
    };
}
现在
wrappedFoo
正在获取一种类型的
(…args:any[])=>any


是否可以让
wrappedFoo
模拟其包装的函数类型?

这是可能的,但如果希望能够传递不同类型和数量的参数,可能会有点混乱

您的示例可以这样做:

function wrap<A, B, C>(fn: (a: A, b: B) => C) {
    return (a: A, b: B): C => {
        return fn(a, b);
    };
}
Is
(a:string,b:number)=>[string,number]

()

但是,正如您所看到的,如果您希望能够使用不同的签名,那么使用它就不是很舒服(例如,我的示例仅适用于两个参数)

您可以只传递一个由接口支持的参数:

function wrap<In, Out>(fn: (params: In) => Out) {
    return (params: In): Out => {
        return fn(params);
    };
}

interface FooParams {
    a: string;
    b: number;
}

function foo(params: FooParams): [string, number] {
    return [params.a, params.b];
}

const wrappedFoo = wrap(foo);
函数换行(fn:(参数:In)=>Out){
返回(参数:输入):输出=>{
返回fn(参数);
};
}
接口参数{
a:弦;
b:数字;
}
函数foo(params:FooParams):[字符串,数字]{
返回[参数a,参数b];
}
const wrappedFoo=wrap(foo);
()


在我看来,这将更容易使用。

可以创建一个包装函数,它接受并返回与其包装的函数相同的类型,只需做2个更改

  • 将包装函数的返回值指定为要包装的
    T
    泛型
  • 将要返回的函数强制转换为
  • 例如:

    function wrap<T extends Function>(fn: T): T {
        return <any>function(...args) {
            return fn(...args)
        };
    }
    
    可以使用为包含0、1、2、3、4或更多参数的包装函数提供特定类型。如果您的一个函数需要更多的参数,请添加一个额外的重载,或者让它回退到rest参数的情况

    function wrap<TResult>(fn: () => TResult) : () => TResult;
    function wrap<T1, TResult>(fn: (param1 : T1) => TResult) : (param1 : T1) => TResult;
    function wrap<T1, T2, TResult>(fn: (param1 : T1, param2 : T2) => TResult) : (param1 : T1, param2 : T2) => TResult;
    function wrap<T1, T2, T3, TResult>(fn: (param1 : T1, param2 : T2, param3 : T3) => TResult) : (param1 : T1, param2 : T2, param3 : T3) => TResult;
    function wrap<T1, T2, T3, T4, TResult>(fn: (param1 : T1, param2 : T2, param3 : T3, param4 : T4) => TResult) : (param1 : T1, param2 : T2, param3 : T3, param4 : T4) => TResult;
    function wrap<TParam, TResult>(fn: (...params : TParam[]) => TResult) : (...params : TParam[]) => TResult {
        return (...params) => {
            return fn(...params);
        };
    }
    
    函数包装(fn:()=>TResult):()=>TResult;
    函数换行(fn:(param1:T1)=>TResult:(param1:T1)=>TResult;
    函数换行(fn:(param1:T1,param2:T2)=>TResult:(param1:T1,param2:T2)=>TResult;
    函数换行(fn:(param1:T1,param2:T2,param3:T3)=>TResult:(param1:T1,param2:T2,param3:T3)=>TResult;
    函数换行(fn:(param1:T1,param2:T2,param3:T3,param4:T4)=>TResult:(param1:T1,param2:T2,param3:T3,param4:T4)=>TResult;
    函数换行(fn:(…参数:TParam[])=>TResult:(…参数:TParam[])=>TResult{
    返回(…参数)=>{
    返回fn(…参数);
    };
    }
    

    它不是很漂亮,但它提供了最准确的类型。

    这适用于任意数量的参数,并保留所有参数和返回类型

    const wrap = <T extends Array<any>, U>(fn: (...args: T) => U) => {
      return (...args: T): U => fn(...args)
    }
    
    const wrap=(fn:(…args:T)=>U)=>{
    返回(…args:T):U=>fn(…args)
    }
    
    最近需要使用箭头函数来实现这一点,提出了一种比其他答案更容易实现的方法,并解决了扩展运算符和
    符号的问题。迭代器
    (在ts 3.8中测试)


    谢谢你的回答,我不想强迫每个被包装的函数接受一个params对象。。。我一直在胡思乱想,我想我已经找到了解决这个特殊问题的方法…
    …args
    在Typescript中生成了一个
    for循环
    ,您可以通过使用函数局部变量
    arguments
    来避免这个问题。太棒了!对于返回与被包装函数相同的值但不直接返回函数调用的包装器,是否也可能出现类似的情况?这确实令人惊讶。一个小问题是,对any的强制转换确实破坏了返回值的类型测试,这意味着如果您的包装器错误地没有返回与包装函数typescript相同的类型,则无法捕获该类型。我正在寻找一个类似的实现,但其中
    fn
    是可选的,并且默认值为
    ()=>{}
    。这可能吗?我测试了这个,它可以工作,但我不知道为什么。这里有
    T
    的名字吗?我希望
    是指“T型同质元素的数组”,或者,如果参数是
    [string,number]
    则是
    数组
    Array
    怎么能表示“按特定顺序排列的特定系列类型的数组”?我认为不需要
    t
    <代码>应足以满足此要求signature@ElliotNelson@jcalz是的,可以使用
    Array
    代替,我用T绕过我的eslint警告,但是
    Array
    更清晰,谢谢你的反馈!内部函数上的显式返回类型,
    :U
    让我有点困惑。我认为它可以从参数类型推断出来?在VSCode中工作得很好!甚至参数的签名/名称也被保留
    (a: string, b: number) => [string, number].
    
    function wrap<TResult>(fn: () => TResult) : () => TResult;
    function wrap<T1, TResult>(fn: (param1 : T1) => TResult) : (param1 : T1) => TResult;
    function wrap<T1, T2, TResult>(fn: (param1 : T1, param2 : T2) => TResult) : (param1 : T1, param2 : T2) => TResult;
    function wrap<T1, T2, T3, TResult>(fn: (param1 : T1, param2 : T2, param3 : T3) => TResult) : (param1 : T1, param2 : T2, param3 : T3) => TResult;
    function wrap<T1, T2, T3, T4, TResult>(fn: (param1 : T1, param2 : T2, param3 : T3, param4 : T4) => TResult) : (param1 : T1, param2 : T2, param3 : T3, param4 : T4) => TResult;
    function wrap<TParam, TResult>(fn: (...params : TParam[]) => TResult) : (...params : TParam[]) => TResult {
        return (...params) => {
            return fn(...params);
        };
    }
    
    const wrap = <T extends Array<any>, U>(fn: (...args: T) => U) => {
      return (...args: T): U => fn(...args)
    }
    
    type AnyFunction = (...args: any[]) => any;
    
    const wrap = <Func extends AnyFunction>(
      fn: Func,
    ): ((...args: Parameters<Func>) => ReturnType<Func>) => {
      const wrappedFn = (...args: Parameters<Func>): ReturnType<Func> => {
          // your code here
          return fn(...args);
      };
      return wrappedFn;
    };
    
    const foo = (a: string, b: number): number => a.length + b;
    const wrappedFoo = wrap<typeof foo>(foo);
    
    foo('hello', -5); // => 0
    wrappedFoo('hello', -5); // => 0