Search code examples
reactjstypescriptreact-routerjestjsreact-hooks

Mocking react-router-dom for useHistory hook causes the following error - TS2698: Spread types may only be created from object types


I am trying to mock react-router-dom in one of my test cases so that the useHistory hook will function in my tests. I decide to use jest.mock to mock the entire module, and jest.requireActual to preserve the other properties that I may not want to mock.

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useHistory: () => ({
    location: {
      pathname: '/list',
    },
  }),
}));

This is actually derived from one of the highly rated solutions to the following question: How to mock useHistory hook in jest?

However, the TypeScript compiler is flagging the following error on the following line ...jest.requireActual('react-router-dom'),

TS2698: Spread types may only be created from object types.

Interestingly, I only face this issue after updating jest and ts-jest to the latest versions (jest v26). I do not face any of these issues when I was using jest 24.x.x.

"@types/jest": "^26.0.4",
"jest": "^26.1.0",
"ts-jest": "^26.1.1",

Does anyone know how to solve this issue for the latest jest versions?


Solution

  • jest.requireActual returns unknown type that cannot be spread.

    A correct type is:

    import * as ReactRouterDom from 'react-router-dom';
    
    jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom') as typeof ReactRouterDom,
      useHistory: ...,
    }));
    

    A quick fix is any:

    jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom') as any,
      useHistory: ...,
    }));
    

    It's acceptable because it doesn't impair type safety in this case.

    Since react-router-dom is ES module, a more correct way to mock it is:

    jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom') as any,
      __esModule: true,
      useHistory: ...,
    }));