Issue :
I want to do something like this...
const mockFoo = mocked(foo);
mockFoo.mockReturnValue({
valueNeededForTest: 'string' // TypeScript mad! foo returns other values too!
});
But because foo returns a dozen other properties that the test doesn't rely on, so we get errors like this:
Error:(21, 43) TS2345: Argument of type '{ valueNeededForTest: string }' is not assignable to parameter of type '{ buildPath: string; babelReactIntlPath:
string; cachePath: string; coveragePath: string; isCI: boolean; githubHostname: string; githubOrg: string; githubRepo: string; localPublicPath: string; ... 7 more ...; getPathWithoutRepoDirectory:
(pathToShorten: string) => string; }'.
Type '{ valueNeededForTest: string; }' is missing the following properties from type '{ buildPath: string; babelReactIntlPath: string; cachePath: string;
coveragePath: string; isCI: boolean; githubHostname: string; githubOrg: string; githubRepo: string; localPublicPath: string; ... 7 more ...;
getPathWithoutRepoDirectory: (pathToShorten: string) => string; }': buildPath, babelReactIntlPath, cachePath, coveragePath, and 12 more.
Workarounds:
This takes away the nice typing mocked provides:
const mockFoo = mocked(foo);
mockFoo.mockReturnValue(({
valueNeededForTest: 'string'
} as unknown) as ReturnType<foo>);
This isn't always possible, as running the original function can have side effects, or return something that can't be easily merged like a promise, and jest.requireActual's return type is any so it doesn't even solve the original problem.
const mockFoo = mocked(foo);
mockFoo.mockReturnValue({
...jest.requireActual('./foo'),
valueNeededForTest: 'string'
});
Preferred behavior :
A new function called partialMocked (maybe there's a better name?) that allows returnType/resolvedValue/etc to be a deep partial of what it normally returns.
const mockFoo = partialMocked(foo);
mockFoo.mockReturnValue({
valueNeededForTest: 'string' // TypeScript happy, developer happy!
});
Example implementation :
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Array<infer U>
? Array<DeepPartial<U>>
: T[P] extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: DeepPartial<T[P]>
};
type PartialReturnType<T extends MockableFunction> =
ReturnType<T> extends Promise<infer U>
? Promise<DeepPartial<U>>
: ReturnType<T> extends Array<infer P>
? Array<DeepPartial<P>>
: DeepPartial<ReturnType<T>>;
interface MockWithArgs<T extends MockableFunction> extends jest.MockInstance<PartialReturnType<T>, ArgumentsOf<T>> {
new (...args: ConstructorArgumentsOf<T>): T;
(...args: ArgumentsOf<T>): PartialReturnType<T>;
}
This fixed the issues I was having. I'm still fairly new with TypeScript, so I'm not sure if this is a complete solution or the right way to do it, so that's why I made this ticket instead of starting with a pull request.
Thoughts?
Issue :
I want to do something like this...
But because
fooreturns a dozen other properties that the test doesn't rely on, so we get errors like this:Workarounds:
This takes away the nice typing
mockedprovides:This isn't always possible, as running the original function can have side effects, or return something that can't be easily merged like a promise, and
jest.requireActual's return type isanyso it doesn't even solve the original problem.Preferred behavior :
A new function called
partialMocked(maybe there's a better name?) that allowsreturnType/resolvedValue/etc to be a deep partial of what it normally returns.Example implementation :
This fixed the issues I was having. I'm still fairly new with TypeScript, so I'm not sure if this is a complete solution or the right way to do it, so that's why I made this ticket instead of starting with a pull request.
Thoughts?