Edit
See the next comment (https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42067#issuecomment-581483243)
Currently we need to do an implicit cast when mocking a module:
jest.mock('../src/date-pick/get-hours-sao-paulo')
import getHoursSaoPaulo from '../src/date-pick/get-hours-sao-paulo'
const mockedGetHoursSaoPaulo = getHoursSaoPaulo as unknown as jest.Mock<typeof getHoursSaoPaulo>
So when we want to use a function such as mockImplementation we should to write:
mockedGetHoursSaoPaulo.mockImplementation(() => 21)
But for some type definition problem TS says a false-positive error:

saying that we should to write
mockedGetHoursSaoPaulo.mockImplementation(() => () => 21)
but it's wrong!
Maybe we have more of this problem on others functions on jest.Mock.
I noticed another bug:

But is okay to use others types than string in this case.
@macabeus If you look at the type definition of Mock it's this
Notice the type parameter doesn't take the function type but the return type and arguments so your code should be
jest.Mock<ReturnType<typeof getHoursSaoPaulo>, Arguments<typeof getHoursSaoPaulo>>
// Even better make a type alias:
type MockFromFunction<F extends (...args: any[]) => any>
= jest.Mock<ReturnType<F>, Arguments<F>>
Away from keyboard so haven't tested but most probably should work
@devanshj Thank you for fast reply.
It's almost worked, I just needed to replace Arguments to Parameters, so now it's working:
const mockedGetHoursSaoPaulo = getHoursSaoPaulo as jest.Mock<ReturnType<typeof getHoursSaoPaulo>, Parameters<typeof getHoursSaoPaulo>>
mockedGetHoursSaoPaulo.mockImplementation(() => 21)
But it's a messy code... maybe we could replace to work with just typeof getHoursSaoPaulo?
Also, there is this another problem.
Thank you for fast reply.
No problems!
I just needed to replace Arguments to Parameters
Haha yes forgot it's Parameters
But it's a messy code... maybe we could replace to work with just typeof getHoursSaoPaulo
Well yeah the type parameters are a little unintuitive (maybe there might be a good reason for that) but having a breaking change just to fix this would be too much. As I suggested to make a type alias that would be enough I guess.
Also, there is this another problem.
I'll check it out once I'm free :)
I noticed another bug:
But is okay to use others types than string in this case.
@macabeus So I looked into this the problem is spyOn's type doesn't cover the case where the the function passed is a constructor. The reason it still allows "Date" as second parameter and the reason you are told to return string in the mock implementation because Date() (notice no new) returns a string. The type also reflects that.
So this is indeed a bug. I'm sending a PR in a sec. Also it would be great if you rename the issue to be bug in spyOn also link the description to the comment that describes it.
Okay. I did it. Very thank you to improve this package =]
I'm also trying to accomplish the same thing as @devanshj pointed out here but I'm still running into the same error. Wondering if anyone had any ideas?
I'm doing
const mockedDate: Date = new Date(2020, 0);
jest.spyOn(global, 'Date')
.mockImplementationOnce(() => mockedDate);
and getting TS2322: Type 'Date' is not assignable to type 'string'. on the mockedDate.
For now I can get around it by doing (): any => mockedDate but would prefer not to if at all possible!
Okay so there are too many problems at play.
The reason for the error is that Date is also a function so spyOn picks the function overload (3rd one) instead of the class/constructor overload (4th one). Hence the mock implementation asks for the function overload of Date that returns a string instead of Date.
Now one way to "fix" this by simply making typescript ignore the function overload Date like this
const mockedDate: Date = new Date(2020, 0);
type PatchedGlobal = {
Date: new (...args: ConstructorParameters<DateConstructor>) => Date
}
jest.spyOn(global as PatchedGlobal, "Date")
.mockImplementationOnce(() => mockedDate);
Another problem is that the mocked implementation is actually kinda wrong as Date() should return string but yours would return Date. So another fix would be something like this:
function _MockedDate(this: any) {
if (!(this instanceof _MockedDate)) return new Date(2020, 0).toString();
return (Date as any).call(this, 2020, 0)
}
_MockedDate.prototype = Object.create(Date.prototype)
_MockedDate.prototype.constructor = _MockedDate
_MockedDate.__proto__ = Date
const MockedDate = _MockedDate as any as DateConstructor;
test("MockedDate", () => {
expect(typeof MockedDate()).toBe("string")
expect(new MockedDate() instanceof MockedDate).toBe(true)
expect(new MockedDate() instanceof Date).toBe(true)
expect(new MockedDate().toString()).toBe(new Date(2020, 0).toString()) // throws (before even failing) check the update
})
jest.spyOn(global, "Date")
.mockImplementationOnce(MockedDate);
UPDATE: THIS DOESN'T WORK, tbh I have no idea how to make a class that is also a function apparently inheriting native class is hella work as compared to inheriting userland classes smh
The types of spyOn itself are wrong as they don't take into consideration all the overloads of the spied function and classes. This problem would be in all codebases that use Parameters or ReturnType because they take into account only the first overload. So the most idealist scenario is mockImplementationOnce to have a type that enforces to implement all overloads (I'll take a look on how to make this work maybe later sometime)
So the problem is with spyOn itself but even if it had the ideal types MockedDate would still require a lot of anys because how in the world you can have a type like { (): string, new (): Date }, the fact that Date can be called without new and returns a string itself is weird.
So to conclude
Date() (no new) somewhere. If you're sure the code being tested always has new Date() simply go with mockImplementationOnce(() => mockedDate as any) no need of any (no pun intended xD) ceremony.Okay so there are too many problems at play.
- The reason for the error is that
Dateis also a function so spyOn picks the function overload (3rd one) instead of the class/constructor overload (4th one). Hence the mock implementation asks for the function overload ofDatethat returns astringinstead ofDate.
...
@devanshj This is a really good explanation - thanks! I spent a good while trying to figure out where the string was coming from 馃槀
Understand now this is by no means a simple fix and one of the many interesting things of Javascript haha but really appreciate you sending across them workarounds for this issue!
@devanshj This is a really good explanation - thanks! I spent a good while trying to figure out where the
stringwas coming from 馃槀
Understand now this is by no means a simple fix and one of the many interesting things of Javascript haha but really appreciate you sending across them workarounds for this issue!
Haha you're welcome! It indeed has no simple fix.