From de58fc6f0358b395d6fa7e7cbcb52146ed0cd3cc Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 15:14:20 +0300 Subject: [PATCH 1/8] feat: add type inference for assertion --- CHANGELOG.md | 1 + packages/expect/src/types.ts | 16 ++++++++-------- packages/jest-types/__typetests__/expect.test.ts | 3 +++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d559084a4e5d..42038f653f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Features +- `[expect, @jest/expect]` support type inference for `toBe` assertions ([#xxxxx](https://github.com/facebook/jest/pull/xxxxx)) - `[@jest/globals, jest-mock]` Add `jest.Spied*` utility types ([#13440](https://github.com/facebook/jest/pull/13440)) ### Fixes diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index e2fc979165ff..b90f852ebb32 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -94,9 +94,9 @@ export interface BaseExpect { } export type Expect = { - (actual: T): Matchers & - Inverse> & - PromiseMatchers; + (actual: T): Matchers & + Inverse> & + PromiseMatchers; } & BaseExpect & AsymmetricMatchers & Inverse>; @@ -118,20 +118,20 @@ export interface AsymmetricMatchers { stringMatching(sample: string | RegExp): AsymmetricMatcher; } -type PromiseMatchers = { +type PromiseMatchers = { /** * Unwraps the reason of a rejected promise so any other matcher can be chained. * If the promise is fulfilled the assertion fails. */ - rejects: Matchers> & Inverse>>; + rejects: Matchers> & Inverse, T>>; /** * Unwraps the value of a fulfilled promise so any other matcher can be chained. * If the promise is rejected the assertion fails. */ - resolves: Matchers> & Inverse>>; + resolves: Matchers> & Inverse, T>>; }; -export interface Matchers> { +export interface Matchers, T = unknown> { /** * Ensures the last call to a mock function was provided specific args. */ @@ -152,7 +152,7 @@ export interface Matchers> { * Checks that a value is what you expect. It calls `Object.is` to compare values. * Don't use `toBe` with floating-point numbers. */ - toBe(expected: unknown): R; + toBe(expected: T): R; /** * Ensures that a mock function is called. */ diff --git a/packages/jest-types/__typetests__/expect.test.ts b/packages/jest-types/__typetests__/expect.test.ts index 79b68993379b..e0bff58f5ed1 100644 --- a/packages/jest-types/__typetests__/expect.test.ts +++ b/packages/jest-types/__typetests__/expect.test.ts @@ -221,6 +221,9 @@ expectType( ), ); +expectType(expect(3).toBe(5)); +expectError(expect(3).toBe('5')); + expectType(expect(jest.fn()).toHaveBeenCalledWith()); expectType(expect(jest.fn()).toHaveBeenCalledWith(123)); expectType(expect(jest.fn()).toHaveBeenCalledWith(123, 'value')); From 1bfd6f316cd11627d9c183acae6d4d16785f74a3 Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 15:18:16 +0300 Subject: [PATCH 2/8] add type argument --- packages/jest-expect/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-expect/src/types.ts b/packages/jest-expect/src/types.ts index 10739da8481e..d1ba64a7f66f 100644 --- a/packages/jest-expect/src/types.ts +++ b/packages/jest-expect/src/types.ts @@ -29,7 +29,7 @@ type Inverse = { not: Matchers; }; -type JestMatchers, T> = Matchers & +type JestMatchers, T> = Matchers & SnapshotMatchers; type PromiseMatchers = { From 74da09d460f438850fc3310afa248ec0f739c8c6 Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 17:31:42 +0300 Subject: [PATCH 3/8] fix type for promise matchers --- packages/expect/src/types.ts | 5 +++-- packages/jest-expect/src/types.ts | 12 +++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index b90f852ebb32..b3af1a09ee04 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -123,12 +123,13 @@ type PromiseMatchers = { * Unwraps the reason of a rejected promise so any other matcher can be chained. * If the promise is fulfilled the assertion fails. */ - rejects: Matchers> & Inverse, T>>; + rejects: Matchers> & Inverse>>; /** * Unwraps the value of a fulfilled promise so any other matcher can be chained. * If the promise is rejected the assertion fails. */ - resolves: Matchers> & Inverse, T>>; + resolves: Matchers, Awaited> & + Inverse, Awaited>>; }; export interface Matchers, T = unknown> { diff --git a/packages/jest-expect/src/types.ts b/packages/jest-expect/src/types.ts index d1ba64a7f66f..ca0bad317075 100644 --- a/packages/jest-expect/src/types.ts +++ b/packages/jest-expect/src/types.ts @@ -29,7 +29,10 @@ type Inverse = { not: Matchers; }; -type JestMatchers, T> = Matchers & +type JestMatchers, T = unknown> = Matchers< + R, + T +> & SnapshotMatchers; type PromiseMatchers = { @@ -37,14 +40,13 @@ type PromiseMatchers = { * Unwraps the reason of a rejected promise so any other matcher can be chained. * If the promise is fulfilled the assertion fails. */ - rejects: JestMatchers, T> & - Inverse, T>>; + rejects: JestMatchers> & Inverse>>; /** * Unwraps the value of a fulfilled promise so any other matcher can be chained. * If the promise is rejected the assertion fails. */ - resolves: JestMatchers, T> & - Inverse, T>>; + resolves: JestMatchers, Awaited> & + Inverse, Awaited>>; }; declare module 'expect' { From 57d3c49e6c990f350182a828b150fc6366a44265 Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 17:35:37 +0300 Subject: [PATCH 4/8] docs: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42038f653f23..a9e9d7dee890 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- `[expect, @jest/expect]` support type inference for `toBe` assertions ([#xxxxx](https://github.com/facebook/jest/pull/xxxxx)) +- `[expect, @jest/expect]` support type inference for `toBe` assertions ([#13470](https://github.com/facebook/jest/pull/13470)) - `[@jest/globals, jest-mock]` Add `jest.Spied*` utility types ([#13440](https://github.com/facebook/jest/pull/13440)) ### Fixes From 9ec4229cfae1c1f176e7c3805fca33c14e565390 Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 17:43:43 +0300 Subject: [PATCH 5/8] test: improve test cases --- packages/jest-types/__typetests__/expect.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/jest-types/__typetests__/expect.test.ts b/packages/jest-types/__typetests__/expect.test.ts index e0bff58f5ed1..d99b8b433934 100644 --- a/packages/jest-types/__typetests__/expect.test.ts +++ b/packages/jest-types/__typetests__/expect.test.ts @@ -221,8 +221,10 @@ expectType( ), ); -expectType(expect(3).toBe(5)); -expectError(expect(3).toBe('5')); +expectType( + expect({name: 'someName', age: 12}).toBe({name: 'someOtherName', age: 13}), +); +expectError(expect({name: 'someName', age: 12}).toBe({name: 'someOtherName'})); expectType(expect(jest.fn()).toHaveBeenCalledWith()); expectType(expect(jest.fn()).toHaveBeenCalledWith(123)); @@ -476,6 +478,7 @@ declare module 'expect' { interface AsymmetricMatchers { toBeWithinRange(floor: number, ceiling: number): void; } + interface Matchers { toBeWithinRange(floor: number, ceiling: number): R; } From d7c8b174b9e4306fe7a6a62d288b05be97c16be1 Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 17:49:37 +0300 Subject: [PATCH 6/8] change inverse cases to unknown --- packages/expect/src/types.ts | 4 ++-- packages/jest-expect/src/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index b3af1a09ee04..251861bfc1b0 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -95,7 +95,7 @@ export interface BaseExpect { export type Expect = { (actual: T): Matchers & - Inverse> & + Inverse> & PromiseMatchers; } & BaseExpect & AsymmetricMatchers & @@ -129,7 +129,7 @@ type PromiseMatchers = { * If the promise is rejected the assertion fails. */ resolves: Matchers, Awaited> & - Inverse, Awaited>>; + Inverse>>; }; export interface Matchers, T = unknown> { diff --git a/packages/jest-expect/src/types.ts b/packages/jest-expect/src/types.ts index ca0bad317075..01122d2fe839 100644 --- a/packages/jest-expect/src/types.ts +++ b/packages/jest-expect/src/types.ts @@ -46,7 +46,7 @@ type PromiseMatchers = { * If the promise is rejected the assertion fails. */ resolves: JestMatchers, Awaited> & - Inverse, Awaited>>; + Inverse>>; }; declare module 'expect' { From 1f5b4f0ed5509e35b9a8a3f9fc6320603655265e Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 17:51:26 +0300 Subject: [PATCH 7/8] remove newline --- packages/jest-types/__typetests__/expect.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/jest-types/__typetests__/expect.test.ts b/packages/jest-types/__typetests__/expect.test.ts index d99b8b433934..0140dd84b996 100644 --- a/packages/jest-types/__typetests__/expect.test.ts +++ b/packages/jest-types/__typetests__/expect.test.ts @@ -478,7 +478,6 @@ declare module 'expect' { interface AsymmetricMatchers { toBeWithinRange(floor: number, ceiling: number): void; } - interface Matchers { toBeWithinRange(floor: number, ceiling: number): R; } From d7711f46d4ff894f84e58fc0c008fb4ad4b1d3e3 Mon Sep 17 00:00:00 2001 From: Roy Hadad Date: Tue, 18 Oct 2022 17:57:42 +0300 Subject: [PATCH 8/8] lint: fix lint --- packages/jest-types/__typetests__/expect.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jest-types/__typetests__/expect.test.ts b/packages/jest-types/__typetests__/expect.test.ts index 0140dd84b996..7ca8779ed978 100644 --- a/packages/jest-types/__typetests__/expect.test.ts +++ b/packages/jest-types/__typetests__/expect.test.ts @@ -222,9 +222,9 @@ expectType( ); expectType( - expect({name: 'someName', age: 12}).toBe({name: 'someOtherName', age: 13}), + expect({age: 12, name: 'someName'}).toBe({age: 13, name: 'someOtherName'}), ); -expectError(expect({name: 'someName', age: 12}).toBe({name: 'someOtherName'})); +expectError(expect({age: 12, name: 'someName'}).toBe({name: 'someOtherName'})); expectType(expect(jest.fn()).toHaveBeenCalledWith()); expectType(expect(jest.fn()).toHaveBeenCalledWith(123));