Skip to content

Commit 427fe2b

Browse files
feat(@jest/expect): Expose type of actual to Matchers (#13848)
Co-authored-by: Tom Mrazauskas <[email protected]>
1 parent 836157f commit 427fe2b

File tree

6 files changed

+54
-8
lines changed

6 files changed

+54
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Fixes
66

7+
- `[expect, @jest/expect]` Provide type of `actual` as a generic argument to `Matchers` to allow better-typed extensions ([#13848](https://github.com/facebook/jest/pull/13848))
8+
79
### Chore & Maintenance
810

911
- `[*]` make sure to exclude `.eslintcache` from published module ([#13832](https://github.com/facebook/jest/pull/13832))

packages/expect/__typetests__/expect.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import type * as jestMatcherUtils from 'jest-matcher-utils';
2020

2121
type M = Matchers<void>;
22+
type N = Matchers<void, string>;
2223

2324
expectError(() => {
2425
type E = Matchers;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import {expectAssignable, expectError, expectType} from 'tsd-lite';
9+
import {Matchers, expect} from 'expect';
10+
11+
declare module 'expect' {
12+
interface Matchers<R, T> {
13+
toTypedEqual(expected: T): void;
14+
}
15+
}
16+
17+
expectType<void>(expect(100).toTypedEqual(100));
18+
expectType<void>(expect(101).not.toTypedEqual(101));
19+
20+
expectError(() => {
21+
expect(100).toTypedEqual('three');
22+
});

packages/expect/src/types.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ export interface BaseExpect {
9797
}
9898

9999
export type Expect = {
100-
<T = unknown>(actual: T): Matchers<void> &
101-
Inverse<Matchers<void>> &
102-
PromiseMatchers;
100+
<T = unknown>(actual: T): Matchers<void, T> &
101+
Inverse<Matchers<void, T>> &
102+
PromiseMatchers<T>;
103103
} & BaseExpect &
104104
AsymmetricMatchers &
105105
Inverse<Omit<AsymmetricMatchers, 'any' | 'anything'>>;
@@ -121,20 +121,28 @@ export interface AsymmetricMatchers {
121121
stringMatching(sample: string | RegExp): AsymmetricMatcher;
122122
}
123123

124-
type PromiseMatchers = {
124+
type PromiseMatchers<T = unknown> = {
125125
/**
126126
* Unwraps the reason of a rejected promise so any other matcher can be chained.
127127
* If the promise is fulfilled the assertion fails.
128128
*/
129-
rejects: Matchers<Promise<void>> & Inverse<Matchers<Promise<void>>>;
129+
rejects: Matchers<Promise<void>, T> & Inverse<Matchers<Promise<void>, T>>;
130130
/**
131131
* Unwraps the value of a fulfilled promise so any other matcher can be chained.
132132
* If the promise is rejected the assertion fails.
133133
*/
134-
resolves: Matchers<Promise<void>> & Inverse<Matchers<Promise<void>>>;
134+
resolves: Matchers<Promise<void>, T> & Inverse<Matchers<Promise<void>, T>>;
135135
};
136136

137-
export interface Matchers<R extends void | Promise<void>> {
137+
export interface Matchers<R extends void | Promise<void>, T = unknown> {
138+
/**
139+
* T is a type param for the benefit of users who extend Matchers. It's
140+
* intentionally unused and needs to be named T, not _T, for those users.
141+
* This makes sure TypeScript agrees.
142+
*
143+
* @internal
144+
*/
145+
_unusedT(expected: T): R;
138146
/**
139147
* Ensures the last call to a mock function was provided specific args.
140148
*/

packages/jest-expect/__typetests__/jest-expect.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,16 @@ expectError(() => {
2929

3030
expectAssignable<typeof expect>(jestExpect);
3131
expectNotAssignable<typeof jestExpect>(expect);
32+
33+
declare module 'expect' {
34+
interface Matchers<R, T> {
35+
toTypedEqual(expected: T): void;
36+
}
37+
}
38+
39+
expectType<void>(jestExpect(100).toTypedEqual(100));
40+
expectType<void>(jestExpect(101).not.toTypedEqual(101));
41+
42+
expectError(() => {
43+
jestExpect(100).toTypedEqual('three');
44+
});

packages/jest-expect/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type Inverse<Matchers> = {
2929
not: Matchers;
3030
};
3131

32-
type JestMatchers<R extends void | Promise<void>, T> = Matchers<R> &
32+
type JestMatchers<R extends void | Promise<void>, T> = Matchers<R, T> &
3333
SnapshotMatchers<R, T>;
3434

3535
type PromiseMatchers<T = unknown> = {

0 commit comments

Comments
 (0)