typescript 是否可以 Package 函数并保留其类型?

qxgroojn  于 2022-12-27  发布在  TypeScript
关注(0)|答案(6)|浏览(150)

我试图创建一个通用的 Package 器函数,它将 Package 传递给它的任何函数。
最基本的 Package 器函数看起来类似于

function wrap<T extends Function>(fn: T) {
    return (...args) => {
        return fn(...args)
    };
}

我试着用它像:

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

const wrappedFoo = wrap(foo);

现在wrappedFoo正在获取(...args: any[]) => any类型
有没有可能让wrappedFoo模仿它所 Package 的函数的类型?

cunj1qz1

cunj1qz11#

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

const wrap = <T extends Array<any>, U>(fn: (...args: T) => U) => {
  return (...args: T): U => fn(...args)
}
yacmzcpb

yacmzcpb2#

最近需要用箭头函数来做这个,提出了一种比其他答案更容易的方法,并解决了spread运算符和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;
};
  • 需要AnyFunction,这样我们就可以使用rest运算符并取出参数类型/返回类型
  • 然后,使用Func extends AnyFunc可以描述我们特别喜欢的任何函数的类型
  • 如果只说结果是Func,则会出现无法使用不同的子类型示例化 Package 函数的问题,因此我们需要重新构造它
  • Parameters<Func>将任意参数作为元组获取
  • Symbol.iterator问题的解决方法是使用...Array.from(args)而不是...args
  • ReturnType<Func>获取任意返回类型
  • IDE应该能够正确识别生成的 Package 函数的所有类型

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

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

foo('hello', -5); // => 0
wrappedFoo('hello', -5); // => 0
1tu0hz3e

1tu0hz3e3#

通过做两个更改,可以创建一个 Package 器函数,该函数接受并返回与其 Package 的函数相同的类型
1.将 Package 函数的返回值指定为要 Package 的T泛型
1.将返回的函数强制转换为<any>
例如:

function wrap<T extends Function>(fn: T): T {
    return <any>function(...args) {
        return fn(...args)
    };
}

那么const wrappedFoo = wrap(foo);的类型
则正确地为:

(a: string, b: number) => [string, number].
sc4hvdpw

sc4hvdpw4#

这是可能的,但是如果您希望能够传递不同类型和数量的参数,则可能会有点混乱。
你的例子可以这样做:

function wrap<A, B, C>(fn: (a: A, b: B) => C) {
    return (a: A, b: B): C => {
        return fn(a, b);
    };
}

则类型为:

const wrappedFoo = wrap(foo);

(a: string, b: number) => [string, number]
(code在Playground)
但是正如您所看到的,如果您希望能够使用不同的签名,那么使用这种方法并不方便(例如,我的示例只适用于两个参数)。
你能做的就是只传递一个由接口支持的参数:

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);

(code在Playground)
在我看来,这将更容易处理。

nmpmafwu

nmpmafwu5#

您可以使用重载为 Package 具有0、1、2、3、4个或更多参数的函数提供特定类型。如果您的某个函数需要更多参数,请添加额外的重载或让它回退到其余参数的情况。

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);
    };
}

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

8mmmxcuj

8mmmxcuj6#

如果您希望执行类似于用逻辑/条件 Package 异步函数操作:

const wrapAsyncFunc = <A extends Array<any>, R>(
  fn: (...args: A) => Promise<R>
) => {
  return (...args: A): Promise<R> =>
    new Promise<R>((resolve) => {
      // do logic / side effects...
      resolve(fn(...args));
    });
};

相关问题