diff --git a/types/jest/index.d.ts b/types/jest/index.d.ts index bf9f61899643d8..0970a208ac52be 100644 --- a/types/jest/index.d.ts +++ b/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 @@ -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(item: T, deep?: false): MaybeMocked; + /** + * 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(item: T, deep: true): MaybeMockedDeep; /** * Returns the actual module instead of a mock, bypassing all checks on * whether the module should receive a mock implementation or not. @@ -312,6 +331,34 @@ declare namespace jest { virtual?: boolean | undefined; } + type MockableFunction = (...args: any[]) => any; + type MethodKeysOf = { [K in keyof T]: T[K] extends MockableFunction ? K : never }[keyof T]; + type PropertyKeysOf = { [K in keyof T]: T[K] extends MockableFunction ? never : K }[keyof T]; + type ArgumentsOf = T extends (...args: infer A) => any ? A : never; + type ConstructorArgumentsOf = T extends new (...args: infer A) => any ? A : never; + + interface MockWithArgs extends MockInstance, ArgumentsOf> { + new (...args: ConstructorArgumentsOf): T; + (...args: ArgumentsOf): ReturnType; + } + type MaybeMockedConstructor = T extends new (...args: any[]) => infer R + ? MockInstance> + : T; + type MockedFn = MockWithArgs & { [K in keyof T]: T[K] }; + type MockedFunctionDeep = MockWithArgs & MockedObjectDeep; + type MockedObject = MaybeMockedConstructor & { + [K in MethodKeysOf]: T[K] extends MockableFunction ? MockedFn : T[K]; + } & { [K in PropertyKeysOf]: T[K] }; + type MockedObjectDeep = MaybeMockedConstructor & { + [K in MethodKeysOf]: T[K] extends MockableFunction ? MockedFunctionDeep : T[K]; + } & { [K in PropertyKeysOf]: MaybeMockedDeep }; + type MaybeMockedDeep = T extends MockableFunction + ? MockedFunctionDeep + : T extends object // eslint-disable-line @typescript-eslint/ban-types + ? MockedObjectDeep + : T; + // eslint-disable-next-line @typescript-eslint/ban-types + type MaybeMocked = T extends MockableFunction ? MockedFn : T extends object ? MockedObject : T; type EmptyFunction = () => void; type ArgsType = T extends (...args: infer A) => any ? A : never; type ConstructorArgsType = T extends new (...args: infer A) => any ? A : never; diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index 4169455e21c95c..45bdea34269b12 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -531,6 +531,11 @@ class SpyableClass { // $ExpectType SpyInstance || SpyInstance jest.spyOn({ SpyableClass }, "SpyableClass"); +// $ExpectType MockedObject<{}> +jest.mocked({}); +// $ExpectError +jest.mocked(); + interface Type1 { a: number; }