Shallow HOC with Enzyme and TypeScript
Asked Answered
E

2

8

I have a HOC to test, during shallow mount I should to call some class methods:

it('Should not call dispatch', () => {
        const dispatch = jest.fn()
        const WrappedComponent = someHoc(DummyComponent)
        const instance = shallow(
          <WrappedComponent
            dispatch={dispatch}
          />,
        ).instance() as WrappedComponent
        instance.someMethod()
        expect(dispatch).toHaveBeenCalledTimes(0)
})

test works fine but TS compiler throws an error

 Cannot find name 'WrappedComponent'.

And it is right because WrappedComponent is not a type or class, but if I remove the

 as WrappedComponent

line, TS throws an error

Property 'someMethod' does not exist on type 'Component<{}, {}, any>'.

Also, it does not compile if I change that line as

as typeof WrappedComponent

someHoc description:

import ...

interface State {
  /*state*/
}

interface Props {
  dispatch: Dispatch<Action>
  /*props*/
}

export someHoc = <T extends {}>(
  ChildComponent: React.ComponentClass<T>,
) => {
  class Wrapper extends React.PureComponent<T & Props, State> {

    someMethod = () => {
       /*do smth*/
    }

    render() {
      return (
        <div>
          <ChildComponent {...this.props} />
        </div>
      )
    }
  }

  return Wrapper
}

How can I type the HOC instance? Thanks.

Esmond answered 16/10, 2018 at 19:56 Comment(2)
What's someHoc? How is it typed? Also, it does not compile if I change that line as - what is the error?Such
@estus just added some descriptionEsmond
S
6

It is expected that functions that have variable return value types that can be parametrized are generics. shallow is a generic:

export function shallow<C extends Component, P = C['props'], S = C['state']>(node: ReactElement<P>, options?: ShallowRendererProps): ShallowWrapper<P, S, C>;
export function shallow<P>(node: ReactElement<P>, options?: ShallowRendererProps): ShallowWrapper<P, any>;
export function shallow<P, S>(node: ReactElement<P>, options?: ShallowRendererProps): ShallowWrapper<P, S>;

It likely should be used as:

const instance = shallow<typeof WrappedComponent>(
  <WrappedComponent
    dispatch={dispatch}
  />,
).instance();

There currently seem to be problems in Enzyme typings with using generic parameters to infer component type in ShallowWrapper.

A workaround that secures type safety down the test is to assert the type:

const instance = shallow(
  <WrappedComponent
    dispatch={dispatch}
  />,
)
.instance() as any as InstanceType<typeof WrappedComponent>;
Such answered 16/10, 2018 at 21:19 Comment(5)
the same error ` Cannot find name 'WrappedComponent'.` And Property 'someMethod' does not exist on type 'Component<{}, {}, any>'. if I use typeof WrappedComponentEsmond
Works if do shallow<typeof WrappedComponent> and .instance() as any, thanks!Esmond
Yes, that was a typo, it's typeof. On second thought, this looks like a bug in Enzyme typings. I was just about to suggest to type it as .instance() as any as typeof WrappedComponent and move along.Such
.instance() as any as typeof WrappedComponent is not working, so yes, looks like as some kind of bug.Esmond
Sorry, sure, it's InstanceType<typeof WrappedComponent>, since typeof WrappedComponent is the constructor itself. Updated for clarity.Such
I
0

This is not about HOC, but in case anyone finds this after Googling for hours, try updating @types/react to the latest version (I went from 18.0.14 to 18.2.38).

Which allowed me to use (unmodified)

const about = shallow(<About />);

given

export default function About() {
  const foo = true;
  return (
    <div>Blabla</div>
  )
}

with no problems.

Iamb answered 21/11, 2023 at 17:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.