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
相同的函数签名,但另外公开 .calls
、hasBeenCalled
和 [=23=,那就太好了].
在我当前的实现中,它似乎知道 hasBeenCalled
和 hasBeenCalledWith
说它们是类型:
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;
}
第一个错误表明可以使用显式泛型参数(例如 fn<number, []>()
)调用函数,而默认值(() => {}
)不会提供所需的 return 类型.为了修复此可选链接,使用了默认值
我创建了一个简单的辅助模拟函数。我目前正在努力使类型正确。这是我目前所拥有的:
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
相同的函数签名,但另外公开 .calls
、hasBeenCalled
和 [=23=,那就太好了].
在我当前的实现中,它似乎知道 hasBeenCalled
和 hasBeenCalledWith
说它们是类型:
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;
}
第一个错误表明可以使用显式泛型参数(例如 fn<number, []>()
)调用函数,而默认值(() => {}
)不会提供所需的 return 类型.为了修复此可选链接,使用了默认值