【问题标题】:Typescript higher order function preserve overload types打字稿高阶函数保留重载类型
【发布时间】:2022-02-12 08:47:08
【问题描述】:

我正在尝试创建一个可以将原始观察者附加到方法的高阶函数。只要输入函数没有过载,它就可以很好地工作,但是我遇到了过载问题。我想找到一种通过高阶函数传递所有重载的方法,以便返回的函数可以像原来的一样运行。这是我目前所拥有的:

我用这样的函数构建了一个观察者,为我提供了一种简单的方法来跟踪函数使用的统计信息。

function makeWatcher() {
  let _callCount = 0;

  function countCall() {
    _callCount = _callCount + 1;
  }
  function getCallCount() {
    return _callCount;
  }

  return {
    countCall,
    getCallCount,
  };
}

然后我有一个高阶函数,它接受一个函数和一个观察者作为输入,并返回一个与观察者交互的函数,然后运行原始函数。

// Generic type assures method signature is inferred. 
function watchMethod<T extends (...args: any[]) => any>(
  func: T,
  watcher: Watcher
): (...funcArgs: Parameters<T>) => ReturnType<T> {

  // Return a new function that has same signature as input function,
  // with added watcher interaction when called
  return (...args: Parameters<T>): ReturnType<T> => {

    // Interact with watcher
    watcher.countCall();

    // execute original function and return results
    const results = func(...args);
    return results;
  };
}

最后一步是将这些组合在一起,为我提供一个具有我可以与之交互的属性的方法,如下所示:

function buildWatchedMethod<T extends (...args: any[]) => any>(inputFunction: T) {
  
  // get a watcher
  const watcher = makeWatcher();

  // set up method watching
  const watchedMethod= watchMethod(inputFunction, watcher);

  // list out properties that should be public
  const externalProperties = {
    getCallCount: watcher.getCallCount
  }

  // assign properties to method
  return Object.assign(watchedMethod, externalProperties);
}

然后我可以将观察者添加到这样的方法中:

function method(input: string){
  console.log(input);
}

const watchedMethod = buildWatchedMethod(method);

watchedMethod("Hello World"); // logs "Hello World"
watchedMethod.getCallCount(); // returns 1 - method was called once
watchedMethod("Hello again"); // logs "Hello Again"
watchedMethod.getCallCount(); // returns 2
watchedMethod.countCall(); // error - not available from outside function

这很好用,保留了函数签名,保留了类型安全 - 直到 buildWatchedMethod 被重载方法调用。它仍然有效,但被监视方法的重载并没有在整个过程中保留,这让 typescript 抱怨。

这似乎是this post 的公认答案中概述的预期行为,但有没有办法通过高阶函数携带重载签名?

这个问题最常出现的地方是当我使用重载很常见的库时。 (我在看着你,Mongoose!)我也在尽量避免嘲笑像 Sinon 这样的库,因为 a)它们有时与 typescript 一起使用会很痛苦,b)我对这个用例的需求非常小,第三派对图书馆的内容往往比我需要的多得多。

欢迎提出任何建议!

【问题讨论】:

    标签: typescript


    【解决方案1】:

    我相信有办法让这项工作发挥作用。

    问题是watchMethod 返回类型...使用Parameters&lt;T&gt; 表示原始函数类型似乎没有捕获重载(因为Parameters 只返回一个参数列表)。但是,如果我们直接返回T,重载可以通过。唯一的问题是 Typescript 会抱怨返回类型,所以你将不得不使用类型断言。如果您愿意这样做,那么以下修改应该解决您的问题:

    function watchMethod<T extends (...args: any[]) => any>(
      func: T,
      watcher: Watcher
    ): T {
    
      // Return a new function that has same signature as input function,
      // with added watcher interaction when called
      return ((...args: Parameters<T>): ReturnType<T> => {
    
        // Interact with watcher
        watcher.countCall();
    
        // execute original function and return results
        const results = func(...args);
        return results;
      }) as T;
    }
    

    【讨论】:

    • 感谢马特澄清这一点,除了那个,我一定尝试了所有排列。这正是我所需要的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    相关资源
    最近更新 更多