Conversation
✅ Deploy Preview for jestjs ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
babel-jest
babel-plugin-jest-hoist
babel-preset-jest
create-jest
@jest/diff-sequences
expect
@jest/expect-utils
jest
jest-changed-files
jest-circus
jest-cli
jest-config
@jest/console
@jest/core
@jest/create-cache-key-function
jest-diff
jest-docblock
jest-each
@jest/environment
jest-environment-jsdom
@jest/environment-jsdom-abstract
jest-environment-node
@jest/expect
@jest/fake-timers
@jest/get-type
@jest/globals
jest-haste-map
jest-jasmine2
jest-leak-detector
jest-matcher-utils
jest-message-util
jest-mock
@jest/pattern
jest-phabricator
jest-regex-util
@jest/reporters
jest-resolve
jest-resolve-dependencies
jest-runner
jest-runtime
@jest/schemas
jest-snapshot
@jest/snapshot-utils
@jest/source-map
@jest/test-result
@jest/test-sequencer
@jest/transform
@jest/types
jest-util
jest-validate
jest-watcher
jest-worker
pretty-format
commit: |
0c6bb5c to
5b86080
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates Jest mock typings to better handle overloaded function signatures, mainly for spyOn methods whose earlier overloads return promises.
Changes:
- Adds
FunctionSignaturesin@jest/expect-utilsand reuses it for overload-aware function parameter extraction. - Updates
jest-mockmock value typings to distribute return/resolve/reject types across overloads. - Adds typetests for overloaded
spyOnbehavior and adjusts existing tests affected by the new implementation typing.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
packages/jest-mock/src/index.ts |
Updates SpiedFunction and MockInstance typings for overloaded functions. |
packages/jest-mock/__typetests__/overloaded-spyOn.test.ts |
Adds typetests for overloaded spyOn scenarios. |
packages/jest-mock/__typetests__/Mocked.test.ts |
Updates function-object mock implementation expectations. |
packages/jest-haste-map/src/lib/__tests__/walk.test.ts |
Adjusts a mocked lstat implementation signature. |
packages/expect-utils/src/types.ts |
Adds exported FunctionSignatures and rewires FunctionParameters. |
packages/expect-utils/src/index.ts |
Re-exports the new type helper. |
CHANGELOG.md |
Adds a changelog entry for the typing fix. |
Comments suppressed due to low confidence (2)
packages/jest-mock/src/index.ts:206
- Broadening
mockImplementation/withImplementationto acceptFunctionImplementation<T>lets callers install a function that no longer satisfies the mocked function type (for example, only one overload, or a function object without its required members). The mock andgetMockImplementation()are still typed asT, so downstream code can call other overloads or access members that the stored implementation does not actually support; keep implementations assignable toT(or otherwise align the stored/getter types) and limit the overload distribution to return/resolve/reject values.
mockImplementation(fn: FunctionImplementation<T>): this;
mockImplementationOnce(fn: FunctionImplementation<T>): this;
withImplementation(
fn: FunctionImplementation<T>,
callback: () => Promise<unknown>,
): Promise<void>;
withImplementation(fn: FunctionImplementation<T>, callback: () => void): void;
packages/jest-mock/typetests/overloaded-spyOn.test.ts:67
- This positive test has the same unsoundness for
mockImplementationOnce: the one-off implementation will be used for whichever overload is called next, but it is only required to satisfy one overload while the spied function remains typed as supporting all overloads.
test('mockImplementationOnce accepts a function matching one overload only', () => {
expect(
spyOn(o, 'callbackable').mockImplementationOnce(() => Promise.resolve()),
).type.toBe<SpiedFunction<typeof o.callbackable>>();
5b86080 to
4a813f1
Compare
SimenB
left a comment
There was a problem hiding this comment.
#15998 was only ever about mockReturnValue/mockResolvedValue/mockRejectedValue , mockImplementation wasn't broken. Widening it here fixes a real but separate annoyance (namespace members / type predicates blocking plain functions, e.g. Array.isArray), at the cost of the much more common overloaded-callback case.
Would rather split this: keep FunctionReturnType/FunctionResolveType/FunctionRejectType + SpiedFunction<T> = MockInstance<T>, revert mockImplementation/withImplementation back to plain fn: T. Fixes #15998 with none of the downside, and you can drop FunctionSignaturesWithThis/FunctionImplementation and the casts they forced into mockReturnThis/_createMockFunction.
Then Array.isArray-style stripping can be its own PR where it's easier to assess than bundled with other more "safe" fixes 🙂
Also: OverloadedReturnType (tfrom #16237) I don't think is needed with this? ResolveType/RejectType only ever get called with an already-distributed single signature via FunctionSignatures. Worth deleting?
| }); | ||
| }); | ||
| .mockImplementation( | ||
| (p: fs.PathLike, cb: Parameters<typeof origLstat>[1]) => { |
There was a problem hiding this comment.
Fixed in 89a0626. This test is restored to its upstream form, including inferred p. Restoring MockInstance implementation APIs to fn: T exposed that SpiedFunction<T> = MockInstance<T> alone breaks this existing lstat implementation and existing Array.isArray spy implementations.
SpiedFunction now retains only the pre-existing collapsed signatures for mockImplementation, mockImplementationOnce, and withImplementation, while keeping all overloads for the return/resolved/rejected value helpers.
@SimenB would you pls check again 😄
soltonigiri
left a comment
There was a problem hiding this comment.
The compatibility overloads on SpiedFunction<T> still lose the distributed
value-helper types when mockImplementation or mockImplementationOnce is
used in a fluent chain.
At 89a0626, this pattern fails with both TypeScript 5.4.5 and 5.9.3:
jest
.spyOn(target, 'callbackable')
.mockImplementation(callbackImplementation)
.mockResolvedValue(undefined);When the collapsed compatibility overload is selected, mockImplementation
returns that overload's MockInstance<(...args: Parameters<T>) => ReturnType<T>> rather than the outer SpiedFunction<T>. The next helper
therefore sees only the final overload: mockReturnValue(Promise.resolve())
expects void, while the resolved and rejected value types become never.
The same three failures occur after mockImplementationOnce.
The equivalent calls type-check when split across statements, so the behavior
currently depends only on whether the calls are chained. I reproduced these
exact six failures in a 78-assertion matrix covering 1, 2, 4, and 15 overloads;
explicit this, predicate, callback, generic, and rest signatures; callable
objects; and the regular and Once variants of the return-value,
resolved-value, and rejected-value helpers. The remaining 72 assertions pass
at 89a0626.
Could we declare the collapsed compatibility signatures explicitly so that
mockImplementation and mockImplementationOnce return SpiedFunction<T>,
and add a fluent-chain type test? This shape made all 78 assertions pass on
TypeScript 5.9.3 and compiled without diagnostics on TypeScript 5.4.5. The
existing related type tests continue to pass.
89a0626 to
555e674
Compare
555e674 to
c6d0c7f
Compare
Summary
Fixes #15998.
spyOntypings collapsed overloaded methods to the last overload, causingmockReturnValue,mockResolvedValue,mockRejectedValue(and their*Oncevariants) to reject values from any earlier overload withTS2345: ... is not assignable to parameter of type 'never'.Minimal repro that previously errored:
The root cause is that the built-in
ReturnType<T>(andParameters<T>) only see the last overload ofT. The consumer-side helpers used bymockResolvedValueetc. inherited that limitation.Fix
Distribute the value types over every overload by extracting all call signatures as a union via a new shared helper
FunctionSignatures<F>in@jest/expect-utils. The existingFunctionParameters<F>is now reimplemented on top of it, andjest-mockgains three small internal helpers built on the same primitive:FunctionReturnType<T>— union of every overload's return typeFunctionResolveType<T>— distributesResolveTypeover every overloadFunctionRejectType<T>— distributesRejectTypeover every overloadThese are used by
mockReturnValue/Once,mockResolvedValue/Once,mockRejectedValue/OnceonMockInstance.mockImplementation,withImplementation, andmock: MockFunctionState<T>deliberately keep using the originalTso user-supplied implementations still have to satisfy all overloads (contravariance preserved).Supports up to 15 overloads, matching the existing
FunctionParametersInternalconvention.Test plan
packages/jest-mock/__typetests__/overloaded-spyOn.test.tscovering the issue's scenarios (mockRejectedValueOnce,mockResolvedValueOnce,mockReturnValueOnceon an overloaded method).yarn tstyche packages/jest-mock packages/expect-utils— 55 tests / 313 assertions pass.yarn test-types(full suite) — 85 tests / 1580 assertions pass.yarn jest packages/jest-mock packages/expect-utils— 350 tests pass.yarn lint,yarn lint-ts-files,yarn lint:prettierclean on changed files.