0
votes

I have an issue with jest not recognizing merged interfaces correctly.

I have a decleration file from a @types/ library install with npm. The implementation is implemented by the environment as a global in the production system and I have no access to it (think window as browser equivalent that is not defined in your code but exists non the less when js runs in a browser).

// @types/Foo/index.d.ts

interface Foo {
  A();
}

I then add more function to that interface using prototype

// src/monkeypatch.ts

declare global {
  interface Foo {
    B();
  }
}
Foo.prototype.B = function() {};

And use it in my code

// main.ts
import 'src/monkeypatch.ts'; // side effects import
// src/Bar.ts

class Bar {
  bar(foo: Foo) {
    foo.B();
  }
}

So far everything works as expected, my editor is happy, tsc is happy and everything runs just fine in production.

Now I want to set up unit tests and mock out the implementation for the unit test, I create an implementation.

// mocks/Foo.ts

class FooImpl implements Partial<Foo> {
  A() {};
}

export { FooIMpl as Foo };

And the test file

// src/Bar.spec.ts

import { Foo } from '../mocks/Foo';
(global as any).Foo = Foo;
import 'monkeypatch';  // patches A with additional methods

import { Bar } from 'Bar';

new Bar().bar(new Foo());

But I am getting the following error as if its not aware of the fact that the intefaces merged or that it was patched:

Argument of type 'FooImpl' is not assignable to parameter of type 'Foo'. Type 'FooImpl' is missing the following properties from type 'Foo': B. ts(2345)

Changing the call site to bar(new Foo() as any); is a workaround and causes the test to pass but it smells horrible and I want this to work correctly, what am I missing?

The other issue is that I seem to have to use Partial to make the compiler happy, otherwise I get the following error: Class 'FooImpl' incorrectly implements interface 'Foo'. Type 'FooImpl' is missing the following properties from type 'Foo': B

1

1 Answers

0
votes

It seems patching is recognized, but your FooImpl is just defining A(){} and not B(){} as declared by yourself in src/monkeypatch.ts.

After the augmentation, Foo becomes

class Foo {
  A() {}
  B() {}
}

So to assign FooImpl to Foo you should implement both.