Description
Version
jest
29.6.4 / jest-mock
29.6.3
Steps to reproduce
- Clone this test repo https://github.com/ollien/jest-mock-typing-repro
- Run
npm ci
- Run
npx jest
, and watch the compilation fail
Expected behavior
Given the following snippet (in the repo, but I am duplicating it here for posterity's sake)
import { MockedObject, ModuleMocker } from 'jest-mock';
class Foo {
bar() {}
}
const moduleMocker = new ModuleMocker(global);
test('my code works', () => {
const mockMetadata = moduleMocker.getMetadata(Foo)!;
const mockClass = moduleMocker.generateFromMetadata(mockMetadata);
const x: MockedObject<Foo> = new mockClass();
})
I would expect to be able to assign the mocked class to x
, as this is what the type signatures imply (and behavior is in reality).
Actual behavior
The code fails to compile, with an error that the class Foo
is not assignable to MockedObject<Foo>
$ npx jest
ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
FAIL ./test.spec.ts
● Test suite failed to run
test.spec.ts:12:9 - error TS2322: Type 'Foo' is not assignable to type 'MockedObject<Foo>'.
Type 'Foo' is not assignable to type '{ bar: MockedFunction<() => void>; }'.
Types of property 'bar' are incompatible.
Type '() => void' is not assignable to type 'MockedFunction<() => void>'.
Type '() => void' is not assignable to type 'MockInstance<() => void>'.
12 const x: MockedObject<Foo> = new mockClass();
~
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.504 s
Additional context
As a preface: I'm fairly sure I'm using ModuleMocker.generateFromMetadata
properly (mostly due to the way this test is written), but it's entirely possible I'm missing something.
I've done some digging into the type declarations of what I'm working with. This is why I think what I'm doing should work:
generateFromMetadata<T>
returns aMocked<T>
.- Because I am passing a
ClassLike
(that is, the class value itself), this resolves to aMockedClass<T>
) - The definition of
MockedClass
implies that constructing the class should return aMocked<InstanceType<T>>
(which I believe to be equivalent to aMockObject<T>
in this context, but changing the type ofx
toMocked<InstanceType<typeof Foo>>
fails to compile for the same reason.)
Though I am not certain, I believe (3) is what causes the issue; a class itself is not a function which returns an instance of itself, but rather its constructor is.
Something else of note: if you remove the single method from the class Foo, the above sample does compile.
Environment
System:
OS: Linux 6.4 Fedora Linux 38 (Thirty Eight)
CPU: (12) x64 Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
Binaries:
Node: 18.16.1 - /usr/bin/node
Yarn: 1.22.17 - ~/.local/npm/bin/yarn
npm: 9.2.0 - ~/.local/npm/bin/npm
npmPackages:
jest: ^29.6.4 => 29.6.4