feat: Type inference in 'have been called with' parameters#15129
feat: Type inference in 'have been called with' parameters#15129SimenB merged 2 commits intojestjs:mainfrom
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 |
SimenB
left a comment
There was a problem hiding this comment.
thanks for working on this!
and thanks for reviewing @mrazauskas 🙏
packages/expect/package.json
Outdated
| "chalk": "^4.0.0", | ||
| "immutable": "^4.0.0" | ||
| "immutable": "^4.0.0", | ||
| "jest-mock": "workspace:*" |
There was a problem hiding this comment.
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.
@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.
| toThrow(expected?: unknown): R; | ||
| } | ||
|
|
||
| /** |
There was a problem hiding this comment.
thanks for the extensive comments 👍
| - `[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.
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.
@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.
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
|
@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.