-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
feat: Type inference in 'have been called with' parameters #15129
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Conversation
✅ Deploy Preview for jestjs ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site configuration. |
34acf3e
to
c515dc0
Compare
Interesting to see some progress in this field. Some time ago I was walking more or less the same path. All was fun until I realised that asymmetric matcher can be also nested. Also that expected value and target arguments even can have different shapes: expect(
jestExpect(
jest.fn<(date: Date, name?: [string, string]) => void>(),
).toHaveBeenCalledWith(jestExpect.any(Date), [
jestExpect.any(String),
jestExpect.any(String),
]),
).type.toBeVoid();
expect(
jestExpect(
jest.fn<(date: Date, name?: [string, string]) => void>(),
).toHaveBeenCalledWith(jestExpect.any(Date), jestExpect.any(Array)),
).type.toBeVoid(); |
@mrazauskas Thanks, this is a good use/test case! I think it's solvable with changing the current type WithAsymmetricMatchers<P extends Array<any>> =
Array<unknown> extends P ? never : {[K in keyof P]: P[K] | AsymmetricMatcher}; to: type WithAsymmetricMatchers<P extends Array<any>> =
Array<unknown> extends P
? never
: {[K in keyof P]: DeepAsymmetricMatcher<P[K]>};
type DeepAsymmetricMatcher<T> = T extends object
? AsymmetricMatcher | {[K in keyof T]: DeepAsymmetricMatcher<T[K]>}
: AsymmetricMatcher | T; I would've preferred that all the asymmetric-matchers-producing functions will return the type that the matcher is supposed to stand for: jestExpect.stringContaining('') // returns `string`
jestExpect.any(Number) // returns `number` As it will allow for even better type checking. However, I'm not sure it's possible, and even if it is, it's likely going to break a lot of things. Do you mind addressing to questions regarding this PR that I posted in the issue?
|
By the way, I updated the example in my previous comment. Finally I recalled that the biggest challenge was to handle optional arguments like |
c26fc92
to
604d3a7
Compare
That didn't work for me.
The code seems to handle this as well. See my updated commit with all the new test cases. |
Ah.. Both I see currently you are importing one type from Some time later I will try out updated types. Really interesting. |
604d3a7
to
7d3c48a
Compare
@mrazauskas That works 😃 I amended my last commit with this addtional change. |
7d3c48a
to
10837e7
Compare
If I got it right, your implementation only supports type inference for the jest.spyOn(globalThis, 'setTimeout');
jest(setTimeout).toHaveBeenLastCalledWith(jest.any(Function), 1000); Not a blocker. That is fine, of course. I just wanted to check if that is by design or this got overlooked? |
10837e7
to
43b166a
Compare
@mrazauskas That's an oversight :) I adjusted the code to support both |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for working on this!
and thanks for reviewing @mrazauskas 🙏
packages/expect/package.json
Outdated
@@ -31,7 +31,8 @@ | |||
"@fast-check/jest": "^1.3.0", | |||
"@jest/test-utils": "workspace:*", | |||
"chalk": "^4.0.0", | |||
"immutable": "^4.0.0" | |||
"immutable": "^4.0.0", | |||
"jest-mock": "workspace:*" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be in dependencies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love that you've added a bunch of tests (thanks!), but this is getting pretty big. maybe we should have a packages/jest-types/__typetests__/expect/mocks.test.ts
or something? or packages/jest-types/__typetests__/expect/toHaveBeenCalled.test.ts
to group by matcher?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SimenB My thoughts exactly :)
I like the idea of having an expect/
directory with a file per function. I'll see about arranging the PR so it's clear which tests were moved and which were added.
@@ -307,3 +308,148 @@ export interface Matchers<R extends void | Promise<void>, T = unknown> { | |||
*/ | |||
toThrow(expected?: unknown): R; | |||
} | |||
|
|||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the extensive comments 👍
@@ -20,6 +20,7 @@ | |||
- `[jest-environment-jsdom]` [**BREAKING**] Upgrade JSDOM to v22 ([#13825](https://github.com/jestjs/jest/pull/13825)) | |||
- `[@jest/environment-jsdom-abstract]` Introduce new package which abstracts over the `jsdom` environment, allowing usage of custom versions of JSDOM ([#14717](https://github.com/jestjs/jest/pull/14717)) | |||
- `[jest-environment-node]` Update jest environment with dispose symbols `Symbol` ([#14888](https://github.com/jestjs/jest/pull/14888) & [#14909](https://github.com/jestjs/jest/pull/14909)) | |||
- `[expect, @jest/expect]` [**BREAKING**] Add type inference for function parameters in `CalledWith` assertions ([#15129](https://github.com/facebook/jest/pull/15129)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this a breaking change? the odl type was just Function
or something?
also, only expect
seems to be impacted? I don't see any changes to @jest/expect
at least 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SimenB I'm thinking it will likely break some existing tests that use "have been called" functions due to incorrect typing, like it did in tests/examples in this repo. I'm not sure what are the full implications of this "tag", so obviously I will let you decide.
As for @jest/expect
, I merely copied it from the last PR that tried to introduce this feature. I can obviously remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just used as a way to catch the reader's eye.
I really should have included a migration guide thing when I started landing breaking changes, where how the breaking change might affect you would be listed 😅 I'll have to add one
43b166a
to
64be9af
Compare
64be9af
to
02ea166
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
@mrazauskas @SimenB Thanks for your help on this PR. I couldn't have done it without you ❤️ Please let me know if you have more concerns. As for the changelog, I'm keeping it for you to decide whether to include the "breaking" tag and whether |
@eyalroth I was playing with updated types. All works as expected. Great job! |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Fixes #15034.
Summary
See #15034.
This feature has been attempted at the past (#13268), but introduced a breaking change (#13337) with overloaded functions, and thus was reverted.
To reach the desired typing -- especially the handling of overloaded functions vs no-args function vs the default
jest.fn()
type -- I eventually found this code and modified it slightly to fit better to the use-case.Test plan
See the tests added to the PR.