wentjun
wentjun

Reputation: 42546

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?

Upvotes: 27

Views: 12296

Answers (3)

Szorcu
Szorcu

Reputation: 21

You can simply do as object to satisfy the Typescript. It's not ideal as the type is not very specific, but it does its job and is definitely better than as any :))

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

Upvotes: 1

Ambrown
Ambrown

Reputation: 361

In case anyone comes across this when using it for typescript instead of react-router-dom, what worked for me is:

import ts from 'typescript';

jest.mock('typescript', () => ({
  ...jest.requireActual('typescript') as Record<string, unknown>,
  nodeModuleNameResolver: ...,
}));

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 222548

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: ...,
}));

Upvotes: 50

Related Questions