具有泛型类型的Typescript Package 函数

jv4diomz  于 2022-11-26  发布在  TypeScript
关注(0)|答案(4)|浏览(179)

如何 Package 函数而不更改其在Typescript中的泛型类型?

function x() {
  console.log('Original Function');
}

function wrapper<T extends Function>(func: T): T {
  // Typescript compiler error:
  // Type '() => void' is not assignable to type 'T'.
  return () => {
    console.log('Wrapped Function');
    func.call(null);
  }
}

const xWrapped = wrapper(x);
xWrapped(); // logged 'Wrapped Function' & 'Original Function'
zazmityj

zazmityj1#

这里有一个替代方法,它保留内部函数的参数并返回类型,而不依赖于变量类型。

function x(message: string): void {
    console.log(`inner ${message}`);
}

export function wrapper<Args extends any[], Return>(
    operation: (...operationParameters: Args) => Return, 
    ...parameters: Args
): Return {
    console.log(`outer `);

    return operation(...parameters);
}

x("abc");
wrapper(x, "xyz");

// output:
//
// inner abc
// outer
// inner xyz

当用x调用wrapper时,TS编译器将其类型推断为function wrapper<[string], void>(operation: (operationParameters_0: string) => void, parameters_0: string): void
如果尝试调用wrapper(x, 123),它会失败,并具有漂亮的类型安全:Argument of type '123' is not assignable to parameter of type 'string'.

oyt4ldly

oyt4ldly2#

Package 函数应该接受一个函数,并返回一个类型签名完全相同的函数。
Typescript无法知道传递的函数接受了多少参数或返回了什么,您的函数隐式假定它接受了0个参数(除了这个)并返回void。
目前还没有一个很好的方法来保存函数签名。不过,在以后的版本中有一个新的建议可以解决这个问题:https://github.com/Microsoft/TypeScript/issues/5453
现在,您可以创建一个如下所示的通用 Package 器。

function wrapper<T extends (...args:any[])=>any>(func: T): T {
  return <T>((...args:any[]) => {
        console.log('Wrapped Function');
        return func(...args);
    });
}

使用Variadic类型的建议,此函数可以写成以下形式

function wrapper<...TArgs,TRet>(func:(...args:...TARGS)=>TRet) {
    return (...args:...TARGS) => {
        console.log("Wrapped function");
        return func(...args);
    }
}

注意这里的主要区别是没有强制类型转换,上面的解决方案必须告诉编译器返回变量和输入变量是同一类型。然而,对于variadic类型,参数本身可以通用类型化。(注意variadic类型目前不在typescript中,当它被包括在内时,上面的代码完全有可能有语法错误)

针对TypeScript 4.3版本的更新: Package 器的已实现提案工作代码将如下所示

type InferArgs<T> = T extends (...t: [...infer Arg]) => any ? Arg : never;
type InferReturn<T> = T extends (...t: [...infer Arg]) => infer Res ? Res : never;

function getWrapper<TFunc extends (...args: any[]) => any>(func: TFunc)
    : (...args: InferArgs<TFunc>) => InferReturn<TFunc> {

    return (...args: InferArgs<TFunc>) => {
        // something before

        try {
            return func(...args);
        } finally {

            // something after;
        }
    };
}

可以在这里找到一些简短但有帮助的关于Variadic元组用法的信息和示例:是的。

ccrfmcuu

ccrfmcuu3#

我更喜欢一个更简单的解决方案,就像贝娄:

function wrapFunction<TArgs extends any[], TReturn>(
  targetFunction: (...parameters: TArgs) => TReturn,
): (...parameters: TArgs) => TReturn {
  return (...parameters: TArgs) => {
    console.log(`Hello, what is your name?`);
    return targetFunction(...parameters);
  };
}

// --------------- Example
const someFunction = (name: string) => {
    console.log(`Hey! My name is ${name}.`);
}

const wrappedFunction = wrapFunction(someFunction);
wrappedFunction("Fábio");

输出量

[LOG]: "Hello, what is your name?" 
[LOG]: "Hey! My name is Fábio."

或者,如果您想要更一般的内容:

export function wrapFunction<TArgs extends any[], TReturn>(
  targetFunction: (...parameters: TArgs) => TReturn,
  wrapperFunction: (...parameters: TArgs) => void,
): (...parameters: TArgs) => TReturn {
  return (...parameters: TArgs) => {
    wrapperFunction(...parameters);
    return targetFunction(...parameters);
  };
}


// --------------- Example
const someFunction = (name: string) => {
    console.log(`Hey! My name is ${name}.`);
}

const wrapperFunction = (name: string) => {
    console.log(`The wrapper - Hey! My name is ${name}.`);
}

const wrappedFunction = wrapFunction(someFunction, wrapperFunction);
wrappedFunction("Fábio");

输出量

[LOG]: "The wrapper - Hey! My name is Fábio." 
[LOG]: "Hey! My name is Fábio."
eqfvzcg8

eqfvzcg84#

下面是我发现的另一种方法,它甚至保留了 Package 函数的泛型:

const newFunction = ((one, two) => {
    // something before
    const result = oldFunction(one, two)
    // something after
    return result
}) as typeof oldFunction

相关问题