ladle icon indicating copy to clipboard operation
ladle copied to clipboard

Implement Symbol.hasInstance for MockDate

Open runjak opened this issue 7 months ago • 3 comments

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 │
└─────────┴─────────────────────────┴──────┘

runjak avatar May 26 '25 15:05 runjak

🦋 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

changeset-bot[bot] avatar May 26 '25 15:05 changeset-bot[bot]

@tajo would you like me to add a Changeset to this PR?

runjak avatar May 28 '25 09:05 runjak

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 avatar Jun 11 '25 11:06 runjak

@runjak makes sense. Can you please add some unit test and changeset? Thanks!

tajo avatar Jul 20 '25 22:07 tajo

@tajo thanks for the reply :pray: I've made the adjustments and hope this fits your requirements :)

runjak avatar Jul 26 '25 20:07 runjak

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.

runjak avatar Sep 09 '25 16:09 runjak