Skip to content

[Bug]: Constructing a mocked class does not return an object typed as a mock, but rather one typed with the original class' type #14458

Open
@ollien

Description

@ollien

Version

jest 29.6.4 / jest-mock 29.6.3

Steps to reproduce

  1. Clone this test repo https://github.com/ollien/jest-mock-typing-repro
  2. Run npm ci
  3. 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:

  1. generateFromMetadata<T> returns a Mocked<T>.
  2. Because I am passing a ClassLike (that is, the class value itself), this resolves to a MockedClass<T>)
  3. The definition of MockedClass implies that constructing the class should return a Mocked<InstanceType<T>> (which I believe to be equivalent to a MockObject<T> in this context, but changing the type of x to Mocked<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 

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions