Repository: jestjs/jest (TypeScript)

Issue:
Include ts-jest mock util functions in jest core
## 🚀 Feature Proposal

This proposal is for the outcome of the discussion in [ts-jest](https://github.com/kulshekhar/ts-jest/issues/1048) that includes`ts-jest` mock util functions in `jest` core.

## Motivation

Users don't have to install `ts-jest` anymore to access these util mocks, just need to install `jest`.

## Pitch

[Suggestion](https://github.com/kulshekhar/ts-jest/issues/1048#issuecomment-482606650) from @thymikee that it should belong to jest core



Hints:
Yeah, I think this is a good idea. There's been some changes to mocks in `@types/jest` as well, would be nice to bring the best features of both into Jest itself (at which point DT could pull the features from us instead).

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a68259ee8883cc9899463fe79a78bb49a9d2f6de/types/jest/index.d.ts#L1042-L1298
Hi, can I take this issue?
Yes please! 🙂
Hi @SimenB, I'm working on this issue and don't have much progress. I have some doubts about the approach that I should follow. Actually I didn't understand your first comment really well. Should I try to "use" definition on `@types/jest` on `jest-mock` package and work on top of it to add `mocked` fn from `ts-jest`?
Has this been abandoned? I can take a look
Include as well another proposal in this area from https://github.com/kulshekhar/ts-jest/issues/1065
got it, will do

Patch:
diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts
--- a/packages/jest-mock/src/index.ts
+++ b/packages/jest-mock/src/index.ts
@@ -32,6 +32,77 @@ export type MockFunctionMetadata<
   length?: number;
 };
 
+export type MockableFunction = (...args: Array<any>) => any;
+export type MethodKeysOf<T> = {
+  [K in keyof T]: T[K] extends MockableFunction ? K : never;
+}[keyof T];
+export type PropertyKeysOf<T> = {
+  [K in keyof T]: T[K] extends MockableFunction ? never : K;
+}[keyof T];
+
+export type ArgumentsOf<T> = T extends (...args: infer A) => any ? A : never;
+
+export type ConstructorArgumentsOf<T> = T extends new (...args: infer A) => any
+  ? A
+  : never;
+export type MaybeMockedConstructor<T> = T extends new (
+  ...args: Array<any>
+) => infer R
+  ? MockInstance<R, ConstructorArgumentsOf<T>>
+  : T;
+export type MockedFunction<T extends MockableFunction> = MockWithArgs<T> & {
+  [K in keyof T]: T[K];
+};
+export type MockedFunctionDeep<T extends MockableFunction> = MockWithArgs<T> &
+  MockedObjectDeep<T>;
+export type MockedObject<T> = MaybeMockedConstructor<T> & {
+  [K in MethodKeysOf<T>]: T[K] extends MockableFunction
+    ? MockedFunction<T[K]>
+    : T[K];
+} & {[K in PropertyKeysOf<T>]: T[K]};
+export 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]>};
+
+export type MaybeMockedDeep<T> = T extends MockableFunction
+  ? MockedFunctionDeep<T>
+  : T extends object
+  ? MockedObjectDeep<T>
+  : T;
+
+export type MaybeMocked<T> = T extends MockableFunction
+  ? MockedFunction<T>
+  : T extends object
+  ? MockedObject<T>
+  : T;
+
+export type ArgsType<T> = T extends (...args: infer A) => any ? A : never;
+export type Mocked<T> = {
+  [P in keyof T]: T[P] extends (...args: Array<any>) => any
+    ? MockInstance<ReturnType<T[P]>, ArgsType<T[P]>>
+    : T[P] extends Constructable
+    ? MockedClass<T[P]>
+    : T[P];
+} & T;
+export type MockedClass<T extends Constructable> = MockInstance<
+  InstanceType<T>,
+  T extends new (...args: infer P) => any ? P : never
+> & {
+  prototype: T extends {prototype: any} ? Mocked<T['prototype']> : never;
+} & T;
+
+export interface Constructable {
+  new (...args: Array<any>): any;
+}
+
+export interface MockWithArgs<T extends MockableFunction>
+  extends MockInstance<ReturnType<T>, ArgumentsOf<T>> {
+  new (...args: ConstructorArgumentsOf<T>): T;
+  (...args: ArgumentsOf<T>): ReturnType<T>;
+}
+
 export interface Mock<T, Y extends Array<unknown> = Array<unknown>>
   extends Function,
     MockInstance<T, Y> {
@@ -1109,9 +1180,19 @@ export class ModuleMocker {
   private _typeOf(value: any): string {
     return value == null ? '' + value : typeof value;
   }
+
+  // the typings test helper
+  mocked<T>(item: T, deep?: false): MaybeMocked<T>;
+
+  mocked<T>(item: T, deep: true): MaybeMockedDeep<T>;
+
+  mocked<T>(item: T, _deep = false): MaybeMocked<T> | MaybeMockedDeep<T> {
+    return item as any;
+  }
 }
 
 const JestMock = new ModuleMocker(global as unknown as typeof globalThis);
 
 export const fn = JestMock.fn.bind(JestMock);
 export const spyOn = JestMock.spyOn.bind(JestMock);
+export const mocked = JestMock.mocked.bind(JestMock);


Test patch:
diff --git a/packages/jest-mock/src/__tests__/index.test.ts b/packages/jest-mock/src/__tests__/index.test.ts
--- a/packages/jest-mock/src/__tests__/index.test.ts
+++ b/packages/jest-mock/src/__tests__/index.test.ts
@@ -9,7 +9,7 @@
 /* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */
 
 import vm, {Context} from 'vm';
-import {ModuleMocker, fn, spyOn} from '../';
+import {ModuleMocker, fn, mocked, spyOn} from '../';
 
 describe('moduleMocker', () => {
   let moduleMocker: ModuleMocker;
@@ -1452,6 +1452,13 @@ describe('moduleMocker', () => {
   });
 });
 
+describe('mocked', () => {
+  it('should return unmodified input', () => {
+    const subject = {};
+    expect(mocked(subject)).toBe(subject);
+  });
+});
+
 test('`fn` and `spyOn` do not throw', () => {
   expect(() => {
     fn();