ts-auto-mock: [Issue] Version 2.3.4 created a type error with the `createMock` function

Subject of the issue

The latest release 2.3.4 related to the issue #458 fixed an error and also created a new one.

Your environment

  • ts-auto-mock version: 2.3.4
  • typescript version: 3.9.7
  • node version: 13.7.0
  • npm version: 6.14.6

Steps to reproduce

Test:

client = createMock<Client>({
   user: {
      setPresence: setPresenceMock,
   },
});

The new error is on user

Error:

TS2322: Type '{ setPresence: jest.Mock<Promise<Presence>, any>; }' is not assignable to type 'PartialObjectDeep<ClientUser>'.
Types of property 'valueOf' are incompatible.
Type '() => Object' is not assignable to type '(() => string) & (() => Object)'.
Type '() => Object' is not assignable to type '() => string'.
Type 'Object' is not assignable to type 'string'.

Fake Client class:

export class Client {
    public user: ClientUser | null;
}

Fake ClientUser class:

export class ClientUser {
    public setPresence(data: PresenceData): Promise<Presence>;
}

Expected behavior

No error.

Actual behavior

Type error.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (17 by maintainers)

Most upvoted comments

Are we okay to close this one? @C0ZEN

I realised that the problem is in how javascript works. When you create a new object with {} you’re actially creating an object with prototype Object, meaning that in the type you also have methods like toString and valueOf.

This means that when you write:

client = createMock<Client>({
   user: {
      setPresence: setPresenceMock,
   },
});

in terms of typings is the same as writing:

client = createMock<Client>({
  user: {
    setPresence: setPresenceMock,
    valueOf: function() { ... },
    toString: function() { ... },
    ...
  },
});

Object is defined like this:

interface Object {
  ...

  /** Returns the primitive value of the specified object. */
  valueOf(): Object;

  ...
}

so this happens:

// type of 
{
  setPresence: setPresenceMock,
}
// is
{
  setPresence: Function,
  valueOf: () => Object,
  ...
}

The type of valueOf doesn’t match the type of valueOf defined in ClientUser (that is valueOf: () => string) and the typings are failing.

All of this to say that the error makes sense. I think the only thing we can do about this is updating PartialDeep to be relaxed when using Object. In my mind we should change the name of the type and make it specific for the override parameter (something like MockOverrideValues. but better). We know that if the property is part of the prototype it doesn’t get copied over: _.merge({ valueOf: function() { return "123"; }}, {}).valueOf() === "123" this means that in PartialDeep when we get to the Object case we should strip it of its prototype properties fixing this bug.

Let me know if I made any mistakes

I don’t think it’s an issue on Discord js. They are entitled to override valueOf. I’ll keep these issues open. We’ll try and find a better approach 😃 Thanks for investigating and reporting those issues.

In my opinion the best approach, for now, its to re implement valueOf. That would also solve the Infinite issue that you are facing and you can remove the casting.


// first solution
  const mock: Client = createMock<Client>({
    user: {
      setPresence: jest.fn(),
      valueOf: jest.fn(),
    },
  });

  // second solution
  const mock2: Client = createMock<Client>();

  (mock2.user as ClientUser).setPresence = jest.fn();

@C0ZEN . I’ve reproduce the issue just using Partial from typescript

interface A {
    valueOf(): number;
    b: string;
  }

/*
Error TS2322: Type '{ b: string; }' is not assignable to type 'Partial<A>'.   The types returned by 'valueOf()' are incompatible between these types.     Type 'Object' is not assignable to type 'number'.
*/
  const a: Partial<A> = {
    b: 'hello 3',
  };


Yeah, I agree! I didn’t realise that this change may impact a lot of scenario, I thought it was just a better type 😃.

I’ll make the Partial Deep more relaxed to avoid these scenarios until we’ve found a better solution

Cool, thanks @C0ZEN.

The issue is related to the default implementation in an object of valueOf.

The deepPartial that has been added in 2.2.4 is much smarter and it does consider functions. objects have by default valueOf.

I’m not sure yet 100% if there is a way to workaround that in the deepPartial. I’ll let you know.

In the meantime I would recommend to cast the type.