Implement Symbol.hasInstance for MockDate
Hey 👋
so I'm enjoying use of both: ladle and date-fns. And this leads me to a situation where I'm using date-fns isValid and isDate to check some data. In particular we've got a component that does date based calculations based on a MockDate.
My understanding is that date-fns depends on the global Date object in code like this:
// node_modules/date-fns/isDate.js
export function isDate(value) {
return (
value instanceof Date ||
(typeof value === "object" &&
Object.prototype.toString.call(value) === "[object Date]")
);
}
So when calculating new dates we get instances of RealDate, and check whether a RealDate is instanceof a MockDate, because MockDate rightly replaces the global date when mocking with ladle.
For inheritance this is usually the wrong way: a parent is not an instance of the child class. My suggestions would be to treat it as that though in case of MockDate with the following snippet:
static [Symbol.hasInstance](instance: unknown): boolean {
return instance instanceof RealDate;
}
I've tested it by test-wise appending (but not comitting) this code to packages/ladle/lib/app/src/mock-date.ts:
let d = new RealDate();
let m = new MockDate();
console.log({ l: m, d });
console.table([
["d instanceof RealDate", d instanceof RealDate],
["m instanceof RealDate", m instanceof RealDate],
["m instanceof MockDate", m instanceof MockDate],
["d instanceof MockDate", d instanceof MockDate],
]);
It leads to output like this:
❯ node packages/ladle/lib/app/src/mock-date.ts
{ l: 2025-05-26T15:36:10.887Z, d: 2025-05-26T15:36:10.887Z }
┌─────────┬─────────────────────────┬──────┐
│ (index) │ 0 │ 1 │
├─────────┼─────────────────────────┼──────┤
│ 0 │ 'd instanceof RealDate' │ true │
│ 1 │ 'm instanceof RealDate' │ true │
│ 2 │ 'm instanceof MockDate' │ true │
│ 3 │ 'd instanceof MockDate' │ true │
└─────────┴─────────────────────────┴──────┘
🦋 Changeset detected
Latest commit: 3c3bf25b184f2a4e01191e98cf90268a49bdcc5b
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 13 packages
| Name | Type |
|---|---|
| @ladle/react | Patch |
| example | Patch |
| test-addons | Patch |
| test-babel | Patch |
| test-config | Patch |
| test-config-ts | Patch |
| test-css | Patch |
| test-decorators | Patch |
| test-playwright | Patch |
| test-programmatic | Patch |
| test-provider | Patch |
| test-baseweb | Patch |
| test-msw | Patch |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
@tajo would you like me to add a Changeset to this PR?
Note that the original MockDate as implemented in https://github.com/boblauer/MockDate/blob/master/src/mockdate.ts#L41 sets the prototype like this:
MockDate.prototype = RealDate.prototype;
With modern JS this leads to the following TypeError:
MockDate.prototype = RealDate.prototype;
^
TypeError: Cannot assign to read only property 'prototype' of function 'class Date extends RealDate {
...<omitted>...
}'
I believe this to be the reason why the prototype line has been omitted from ladles mock date, and it is also what causes the issue in date-fns isDate and when using instanceof between MockDate and Date objects.
That is why I'm asking to adjust the implementation of Symbol.hasInstance.
@runjak makes sense. Can you please add some unit test and changeset? Thanks!
@tajo thanks for the reply :pray: I've made the adjustments and hope this fits your requirements :)
Hey, I hope I'm not bothering you with this and you're doing fine stress-wise.
Just wanted to note that from my perspective all should be fine here and if you like you could merge my changes.