Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jest] adding definitions for mocked function #57776

Merged
merged 8 commits into from Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 48 additions & 1 deletion types/jest/index.d.ts
@@ -1,4 +1,4 @@
// Type definitions for Jest 27.0
// Type definitions for Jest 27.4
// Project: https://jestjs.io/
// Definitions by: Asana (https://asana.com)
// Ivo Stratev <https://github.com/NoHomey>
Expand Down Expand Up @@ -178,6 +178,25 @@ declare namespace jest {
* Mocks a module with an auto-mocked version when it is being required.
*/
function mock(moduleName: string, factory?: () => unknown, options?: MockOptions): typeof jest;

/**
* The mocked test helper provides typings on your mocked modules and even
* their deep methods, based on the typing of its source. It makes use of
* the latest TypeScript feature, so you even have argument types
* completion in the IDE (as opposed to jest.MockInstance).
*
* Note: while it needs to be a function so that input type is changed, the helper itself does nothing else than returning the given input value.
*/
function mocked<T>(item: T, deep?: false): MaybeMocked<T>;
/**
* The mocked test helper provides typings on your mocked modules and even
* their deep methods, based on the typing of its source. It makes use of
* the latest TypeScript feature, so you even have argument types
* completion in the IDE (as opposed to jest.MockInstance).
*
* Note: while it needs to be a function so that input type is changed, the helper itself does nothing else than returning the given input value.
*/
function mocked<T>(item: T, deep: true): MaybeMockedDeep<T>;
/**
* Returns the actual module instead of a mock, bypassing all checks on
* whether the module should receive a mock implementation or not.
Expand Down Expand Up @@ -312,6 +331,34 @@ declare namespace jest {
virtual?: boolean | undefined;
}

type MockableFunction = (...args: any[]) => any;
type MethodKeysOf<T> = { [K in keyof T]: T[K] extends MockableFunction ? K : never }[keyof T];
type PropertyKeysOf<T> = { [K in keyof T]: T[K] extends MockableFunction ? never : K }[keyof T];
type ArgumentsOf<T> = T extends (...args: infer A) => any ? A : never;
k-rajat19 marked this conversation as resolved.
Show resolved Hide resolved
type ConstructorArgumentsOf<T> = T extends new (...args: infer A) => any ? A : never;
k-rajat19 marked this conversation as resolved.
Show resolved Hide resolved

interface MockWithArgs<T extends MockableFunction> extends MockInstance<ReturnType<T>, ArgumentsOf<T>> {
new (...args: ConstructorArgumentsOf<T>): T;
(...args: ArgumentsOf<T>): ReturnType<T>;
}
type MaybeMockedConstructor<T> = T extends new (...args: any[]) => infer R
? MockInstance<R, ConstructorArgumentsOf<T>>
: T;
type MockedFn<T extends MockableFunction> = MockWithArgs<T> & { [K in keyof T]: T[K] };
type MockedFunctionDeep<T extends MockableFunction> = MockWithArgs<T> & MockedObjectDeep<T>;
type MockedObject<T> = MaybeMockedConstructor<T> & {
[K in MethodKeysOf<T>]: T[K] extends MockableFunction ? MockedFn<T[K]> : T[K];
} & { [K in PropertyKeysOf<T>]: T[K] };
type MockedObjectDeep<T> = MaybeMockedConstructor<T> & {
[K in MethodKeysOf<T>]: T[K] extends MockableFunction ? MockedFunctionDeep<T[K]> : T[K];
} & { [K in PropertyKeysOf<T>]: MaybeMockedDeep<T[K]> };
type MaybeMockedDeep<T> = T extends MockableFunction
? MockedFunctionDeep<T>
: T extends object // eslint-disable-line @typescript-eslint/ban-types
? MockedObjectDeep<T>
: T;
// eslint-disable-next-line @typescript-eslint/ban-types
type MaybeMocked<T> = T extends MockableFunction ? MockedFn<T> : T extends object ? MockedObject<T> : T;
type EmptyFunction = () => void;
type ArgsType<T> = T extends (...args: infer A) => any ? A : never;
type ConstructorArgsType<T> = T extends new (...args: infer A) => any ? A : never;
Expand Down
5 changes: 5 additions & 0 deletions types/jest/jest-tests.ts
Expand Up @@ -531,6 +531,11 @@ class SpyableClass {
// $ExpectType SpyInstance<SpyableClass, [number, string]> || SpyInstance<SpyableClass, [a: number, b: string]>
jest.spyOn({ SpyableClass }, "SpyableClass");

// $ExpectType MockedObject<{}>
jest.mocked({});
// $ExpectError
jest.mocked();

interface Type1 {
a: number;
}
Expand Down