Skip to content

Commit

Permalink
feat(@jest/expect): Expose type of actual to Matchers (#13848)
Browse files Browse the repository at this point in the history
Co-authored-by: Tom Mrazauskas <tom@mrazauskas.de>
  • Loading branch information
benjaminjkraft and mrazauskas committed Feb 2, 2023
1 parent 836157f commit 427fe2b
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,8 @@

### Fixes

- `[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))

### Chore & Maintenance

- `[*]` make sure to exclude `.eslintcache` from published module ([#13832](https://github.com/facebook/jest/pull/13832))
Expand Down
1 change: 1 addition & 0 deletions packages/expect/__typetests__/expect.test.ts
Expand Up @@ -19,6 +19,7 @@ import {
import type * as jestMatcherUtils from 'jest-matcher-utils';

type M = Matchers<void>;
type N = Matchers<void, string>;

expectError(() => {
type E = Matchers;
Expand Down
22 changes: 22 additions & 0 deletions packages/expect/__typetests__/expectTyped.test.ts
@@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {expectAssignable, expectError, expectType} from 'tsd-lite';
import {Matchers, expect} from 'expect';

declare module 'expect' {
interface Matchers<R, T> {
toTypedEqual(expected: T): void;
}
}

expectType<void>(expect(100).toTypedEqual(100));
expectType<void>(expect(101).not.toTypedEqual(101));

expectError(() => {
expect(100).toTypedEqual('three');
});
22 changes: 15 additions & 7 deletions packages/expect/src/types.ts
Expand Up @@ -97,9 +97,9 @@ export interface BaseExpect {
}

export type Expect = {
<T = unknown>(actual: T): Matchers<void> &
Inverse<Matchers<void>> &
PromiseMatchers;
<T = unknown>(actual: T): Matchers<void, T> &
Inverse<Matchers<void, T>> &
PromiseMatchers<T>;
} & BaseExpect &
AsymmetricMatchers &
Inverse<Omit<AsymmetricMatchers, 'any' | 'anything'>>;
Expand All @@ -121,20 +121,28 @@ export interface AsymmetricMatchers {
stringMatching(sample: string | RegExp): AsymmetricMatcher;
}

type PromiseMatchers = {
type PromiseMatchers<T = unknown> = {
/**
* Unwraps the reason of a rejected promise so any other matcher can be chained.
* If the promise is fulfilled the assertion fails.
*/
rejects: Matchers<Promise<void>> & Inverse<Matchers<Promise<void>>>;
rejects: Matchers<Promise<void>, T> & Inverse<Matchers<Promise<void>, 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<Promise<void>> & Inverse<Matchers<Promise<void>>>;
resolves: Matchers<Promise<void>, T> & Inverse<Matchers<Promise<void>, T>>;
};

export interface Matchers<R extends void | Promise<void>> {
export interface Matchers<R extends void | Promise<void>, T = unknown> {
/**
* T is a type param for the benefit of users who extend Matchers. It's
* intentionally unused and needs to be named T, not _T, for those users.
* This makes sure TypeScript agrees.
*
* @internal
*/
_unusedT(expected: T): R;
/**
* Ensures the last call to a mock function was provided specific args.
*/
Expand Down
13 changes: 13 additions & 0 deletions packages/jest-expect/__typetests__/jest-expect.test.ts
Expand Up @@ -29,3 +29,16 @@ expectError(() => {

expectAssignable<typeof expect>(jestExpect);
expectNotAssignable<typeof jestExpect>(expect);

declare module 'expect' {
interface Matchers<R, T> {
toTypedEqual(expected: T): void;
}
}

expectType<void>(jestExpect(100).toTypedEqual(100));
expectType<void>(jestExpect(101).not.toTypedEqual(101));

expectError(() => {
jestExpect(100).toTypedEqual('three');
});
2 changes: 1 addition & 1 deletion packages/jest-expect/src/types.ts
Expand Up @@ -29,7 +29,7 @@ type Inverse<Matchers> = {
not: Matchers;
};

type JestMatchers<R extends void | Promise<void>, T> = Matchers<R> &
type JestMatchers<R extends void | Promise<void>, T> = Matchers<R, T> &
SnapshotMatchers<R, T>;

type PromiseMatchers<T = unknown> = {
Expand Down

0 comments on commit 427fe2b

Please sign in to comment.