TypeScript:输入模拟函数

TypeScript: typing a mock function

我创建了一个简单的辅助模拟函数。我目前正在努力使类型正确。这是我目前所拥有的:

import includes from 'ramda/src/includes';

function fn<T, Y extends any[]> (implementation: ((...args: Y) => T) = () => {}) {
  const mockFn = (...args: Y) => {
    mockFn.calls.push(args);
    return implementation(...args);
  };
  mockFn.calls = [];

  mockFn.hasBeenCalled = () => mockFn.calls.length > 0;
  mockFn.hasBeenCalledWith = (...args: Y) => includes(args, mockFn.calls);

  return mockFn;
}

Here 是一个游乐场示例。

TypeScript 在两个地方抱怨。

首先,它抱怨 implementation 说:

Type '() => void' is not assignable to type '(...args: Y) => T'.
  Type 'void' is not assignable to type 'T'.
    'T' could be instantiated with an arbitrary type which could be unrelated to 'void'.

其次,它抱怨 mockFn.calls

Member 'calls' implicitly has an 'any[]' type.

模拟函数应该这样使用:

// with implementation
const originalFunction = (a: number, b: number) => a + b; // e.g. a simple add function
const mockedFn = fn(originalFunction);

mockedFn.hasBeenCalled();
// false

mockedFn(21, 21);
// 42

mockedFn.hasBeenCalled();
// true

mockedFn.hasBeenCalledWith(21);
// false

mockedFn.hasBeenCalledWith(21, 21);
// true

但它也应该在没有实现的情况下工作(因此默认为 () => {})。

const mockFn = fn();

// etc.

如果 TypeScript 能够知道 mockedFn 具有与 originalFunction 相同的函数签名,但另外公开 .callshasBeenCalled 和 [=23=,那就太好了].

在我当前的实现中,它似乎知道 hasBeenCalledhasBeenCalledWith 说它们是类型:

mockFn.hasBeenCalled(): boolean
mockFn.hasBeenCalledWith(...args: Y): boolean

如何修复这些类型错误,以便 TypeScript 了解 fn 的功能?

您可以使用表示函数的单个通用参数并使用 Parameters and ReturnType 实用程序:

function fn<T extends (...args: any[]) => any> (implementation?: T) {
  const mockFn = (...args: Parameters<T>): ReturnType<T> => {
    mockFn.calls.push(args);
    return implementation?.(...args);
  };
  mockFn.calls = [] as (Parameters<T>)[];

  mockFn.hasBeenCalled = () => mockFn.calls.length > 0;
  mockFn.hasBeenCalledWith = (...args: Parameters<T>) => includes(args, mockFn.calls);

  return mockFn;
}

Playground


第一个错误表明可以使用显式泛型参数(例如 fn<number, []>())调用函数,而默认值(() => {})不会提供所需的 return 类型.为了修复此可选链接,使用了默认值