Skip to content

Commit

Permalink
Merge pull request #32816 from antoinebrault/jest-type-inference
Browse files Browse the repository at this point in the history
[jest] fix type inference for Mocked & mockResolvedValue/mockRejectedValue
  • Loading branch information
minestarks committed Feb 12, 2019
2 parents c8792a3 + 8fbb276 commit d29109f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 11 deletions.
8 changes: 4 additions & 4 deletions types/jest-when/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export interface WhenMock<T = any, Y extends any[] = any> extends jest.Mock<T, Y
expectCalledWith(...matchers: Y): WhenMock<T, Y>;
mockReturnValue(value: T): WhenMock<T, Y>;
mockReturnValueOnce(value: T): WhenMock<T, Y>;
mockResolvedValue(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockResolvedValueOnce(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockRejectedValue(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockRejectedValueOnce(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockResolvedValue(value: jest.ResolvedValue<T>): WhenMock<T, Y>;
mockResolvedValueOnce(value: jest.ResolvedValue<T>): WhenMock<T, Y>;
mockRejectedValue(value: jest.RejectedValue<T>): WhenMock<T, Y>;
mockRejectedValueOnce(value: jest.RejectedValue<T>): WhenMock<T, Y>;
}

export type When = <T, Y extends any[]>(fn: jest.Mock<T, Y>) => WhenMock<T, Y>;
Expand Down
15 changes: 8 additions & 7 deletions types/jest/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ declare var xtest: jest.It;

declare const expect: jest.Expect;

type ArgsType<T> = T extends (...args: infer A) => any ? A : never;

interface NodeRequire {
/**
* Returns the actual module instead of a mock, bypassing all checks on
Expand Down Expand Up @@ -237,6 +235,9 @@ declare namespace jest {
}

type EmptyFunction = () => void;
type ArgsType<T> = T extends (...args: infer A) => any ? A : never;
type RejectedValue<T> = T extends PromiseLike<any> ? any : never;
type ResolvedValue<T> = T extends PromiseLike<infer U> ? U | T : never;
// see https://github.com/Microsoft/TypeScript/issues/25215
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K }[keyof T] & string;
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never }[keyof T] & string;
Expand Down Expand Up @@ -807,7 +808,7 @@ declare namespace jest {
* myApi.myApiMethod.mockImplementation(() => "test");
*/
type Mocked<T> = {
[P in keyof T]: T[P] & MockInstance<T[P], ArgsType<T[P]>>;
[P in keyof T]: T[P] extends (...args: any[]) => any ? MockInstance<ReturnType<T[P]>, ArgsType<T[P]>>: T[P];
} & T;

interface MockInstance<T, Y extends any[]> {
Expand Down Expand Up @@ -915,7 +916,7 @@ declare namespace jest {
/**
* Simple sugar function for: `jest.fn().mockImplementation(() => Promise.resolve(value));`
*/
mockResolvedValue(value: T | PromiseLike<T>): Mock<Promise<T>, Y>;
mockResolvedValue(value: ResolvedValue<T>): Mock<T, Y>;
/**
* Simple sugar function for: `jest.fn().mockImplementationOnce(() => Promise.resolve(value));`
*
Expand All @@ -935,7 +936,7 @@ declare namespace jest {
* });
*
*/
mockResolvedValueOnce(value: T | PromiseLike<T>): Mock<Promise<T>, Y>;
mockResolvedValueOnce(value: ResolvedValue<T>): Mock<T, Y>;
/**
* Simple sugar function for: `jest.fn().mockImplementation(() => Promise.reject(value));`
*
Expand All @@ -947,7 +948,7 @@ declare namespace jest {
* await asyncMock(); // throws "Async error"
* });
*/
mockRejectedValue(value: any): Mock<Promise<T>, Y>;
mockRejectedValue(value: RejectedValue<T>): Mock<T, Y>;

/**
* Simple sugar function for: `jest.fn().mockImplementationOnce(() => Promise.reject(value));`
Expand All @@ -965,7 +966,7 @@ declare namespace jest {
* });
*
*/
mockRejectedValueOnce(value: any): Mock<Promise<T>, Y>;
mockRejectedValueOnce(value: RejectedValue<T>): Mock<T, Y>;
}

/**
Expand Down
55 changes: 55 additions & 0 deletions types/jest/jest-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,61 @@ const spy6 = jest.spyOn(spiedTarget2, "value", "set");

// should compile
jest.fn().mockImplementation((test: number) => test);
jest.fn().mockResolvedValue(1);

interface Type1 { a: number; }
interface Type2 { b: number; }
class TestMocked {
field: string;
test1(x: Type1): Promise<Type1> {
return Promise.resolve(x);
}
test2(x: Promise<Type1>): Promise<Type1> {
return x;
}
test3(x: Promise<Type1>): Promise<Type2> {
return x.then(() => ({ b: 1 }));
}
test4(x: Type1): Type1 {
return x;
}
}

const mocked: jest.Mocked<TestMocked> = new TestMocked() as any;
mocked.test1.mockImplementation(() => Promise.resolve({ a: 1 }));
mocked.test1.mockReturnValue(Promise.resolve({ a: 1 }));
// $ExpectType Mock<Promise<Type1>, [Type1]>
mocked.test1.mockResolvedValue({ a: 1 });
mocked.test1.mockResolvedValueOnce({ a: 1 });
// $ExpectType Mock<Promise<Type1>, [Type1]>
mocked.test1.mockResolvedValue(Promise.resolve({ a: 1 }));
mocked.test1.mockResolvedValueOnce(Promise.resolve({ a: 1 }));
// $ExpectType Mock<Promise<Type1>, [Promise<Type1>]>
mocked.test2.mockResolvedValue({ a: 1 });
mocked.test2.mockResolvedValueOnce({ a: 1 });
// $ExpectType Mock<Promise<Type1>, [Promise<Type1>]>
mocked.test2.mockResolvedValue(Promise.resolve({ a: 1 }));
mocked.test2.mockResolvedValueOnce(Promise.resolve({ a: 1 }));
// $ExpectType Mock<Promise<Type2>, [Promise<Type1>]>
mocked.test3.mockResolvedValue({ b: 1 });
mocked.test3.mockResolvedValueOnce({ b: 1 });
// $ExpectType Mock<Promise<Type2>, [Promise<Type1>]>
mocked.test3.mockResolvedValue(Promise.resolve({ b: 1 }));
mocked.test3.mockResolvedValueOnce(Promise.resolve({ b: 1 }));
mocked.test3.mockRejectedValue(new Error());
mocked.test3.mockRejectedValueOnce(new Error());
// $ExpectError
mocked.test4.mockResolvedValue({ a: 1 });
// $ExpectError
mocked.test4.mockResolvedValueOnce({ a: 1 });
// $ExpectError
mocked.test4.mockResolvedValue(Promise.resolve({ a: 1 }));
// $ExpectError
mocked.test4.mockResolvedValueOnce(Promise.resolve({ a: 1 }));
// $ExpectError
mocked.test4.mockRejectedValue(new Error());
// $ExpectError
mocked.test4.mockRejectedValueOnce(new Error());

/* Snapshot serialization */

Expand Down

0 comments on commit d29109f

Please sign in to comment.