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

tik*_*zky 11 typescript

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

在最基本的包装函数看起来像

function wrap<T extends Function>(fn: T) {
    return (...args) => {
        return fn(...args)
    };
}
Run Code Online (Sandbox Code Playgroud)

我想尝试使用它:

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

const wrappedFoo = wrap(foo);
Run Code Online (Sandbox Code Playgroud)

现在wrappedFoo正在获得一种类型(...args: any[]) => any

是否有可能wrappedFoo模仿其包装功能的类型?

for*_*010 14

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

const wrap = <T extends Array<any>, U>(fn: (...args: T) => U) => {
  return (...args: T): U => fn(...args)
}
Run Code Online (Sandbox Code Playgroud)

  • 我对此进行了测试,但确实不知道为什么。这里有“ T”的名字吗?我希望`&lt;T,U将Array &lt;T &gt;&gt;'扩展为“类型为T的同类元素的数组”,或者,如果参数为[[string,number]`,则为`Array &lt;string | number&gt;`。 。“ Array &lt;T&gt;”如何表示“按特定顺序排列的一系列特定类型的数组”? (2认同)
  • 我认为不需要 `T`...`&lt;U extends any[], V&gt;` 对于这个签名来说应该足够了 (2认同)
  • 我发现将 `Array&lt;any&gt;` 更改为 `Array&lt;unknown&gt;` 可以避免 linting 规则 no-explicit-any (2认同)

tik*_*zky 13

可以创建一个包装函数,通过进行2次更改来接受并返回与包装函数相同的类型

  1. 将包装函数的返回值指定为要包装的T泛型
  2. 施放你要返回的功能 <any>

例如:

function wrap<T extends Function>(fn: T): T {
    return <any>function(...args) {
        return fn(...args)
    };
}
Run Code Online (Sandbox Code Playgroud)

那么类型 const wrappedFoo = wrap(foo);

然后将正确地:

(a: string, b: number) => [string, number].
Run Code Online (Sandbox Code Playgroud)


Pau*_* S. 8

最近需要使用箭头函数来做到这一点,想出了一种比其他一些答案更容易工作的方法,并解决了扩展运算符的问题和Symbol.iterator(在 ts 3.8 中测试)

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;
};
Run Code Online (Sandbox Code Playgroud)
  • AnyFunction 需要这样我们就可以使用 rest 运算符并提取参数类型/返回类型
    • 使用Func extends AnyFuncthen 让我们描述我们特别喜欢的任何函数的类型
  • 只是说结果是Func围绕包装函数无法用不同的子类型实例化的问题,因此我们需要重新构造它
    • Parameters<Func> 获取任意参数作为元组
      • Symbol.iterator问题的具体解决方法是使用...Array.from(args)而不是...args
    • ReturnType<Func> 获取任意返回类型
  • 您的 IDE 应该正确识别生成的包装函数的所有类型

包装函数现在将具有与内部函数相同的签名,几乎可以执行任何操作

const foo = (a: string, b: number): number => a.length + b;
const wrappedFoo = wrap<typeof foo>(foo);

foo('hello', -5); // => 0
wrappedFoo('hello', -5); // => 0
Run Code Online (Sandbox Code Playgroud)


Nit*_*mer 3

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

你的例子可以这样完成:

function wrap<A, B, C>(fn: (a: A, b: B) => C) {
    return (a: A, b: B): C => {
        return fn(a, b);
    };
}
Run Code Online (Sandbox Code Playgroud)

那么类型为:

const wrappedFoo = wrap(foo);
Run Code Online (Sandbox Code Playgroud)

(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);
Run Code Online (Sandbox Code Playgroud)

操场上的代码

我认为这会更容易使用。