2

I have a custom hook useForm written in TS and it works just fine. Testing it with @testing-library/react-hooks. The tests pass. But typescript is complaining in a one specific place - see below:

Here is the custom hooks:

import { useState } from 'react';

export function useForm<T>(initialValues: T) {
  const [values, setValues] = useState(initialValues);

  return {
    values: values,
    handleChange: (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
      setValues({
        ...values,
        [e.target.name]: e.target.value,
      });
    },
    reset: () => setValues(initialValues),
  };
}

Here is the test:

import { renderHook, act } from '@testing-library/react-hooks';

import { useForm } from './useForm';

test('should render useForm hook', () => {
  const { result } = renderHook(() =>
    useForm({
      text: 'Note One',
    }),
  );

  expect(result.current).toBeDefined();
  expect(result.current.values.text).toBe('Note One');

  act(() => {
    result.current.handleChange({
      target: { //complains here - Type '{ name: string; value: string; }' is not assignable to type 'EventTarget & (HTMLTextAreaElement | HTMLInputElement)'.
        name: 'text',
        value: 'Note Two',
      },
    });
  });

  expect(result.current.values.text).toBe('Note Two');

  act(() => {
    result.current.reset();
  });
  expect(result.current.values.text).toBe('Note One');
});

Here is a working code sandbox: https://codesandbox.io/s/flamboyant-curie-6i5moy?file=/src/useForm.test.tsx

2
  • Using as any is really the only way to get around this, I think. There are over +300 properties required by the type. Commented Mar 1, 2022 at 20:06
  • @beautifulcoder Have tried casting it to as any. Then it says that any is not assignable to EventTarget & (HTMLTextAreaElement | HTMLInputElement) Commented Mar 2, 2022 at 0:25

3 Answers 3

2

I will create a change event by using Event() constructor. And set the event target for this event by using Object.defineProperty(event, 'target', {value: {}, writable: false}), also see this answer. Then use type casting event as unknown as React.ChangeEvent<HTMLInputElement> cast the event.

E.g.

import "@testing-library/react-hooks/lib/dom/pure";
import { renderHook, act } from "@testing-library/react-hooks";

import { useForm } from "./useForm";

test("should render useForm hook", () => {
  const { result } = renderHook(() =>
    useForm({
      text: "Note One"
    })
  );

  expect(result.current).toBeDefined();
  expect(result.current.values.text).toBe("Note One");

  const event = new Event("change");
  Object.defineProperty(event, "target", {
    value: {
      name: "text",
      value: "Note Two"
    },
    writable: false
  });

  act(() => {
    result.current.handleChange(event as unknown as React.ChangeEvent<HTMLInputElement>);
  });

  expect(result.current.values.text).toBe("Note Two");

  act(() => {
    result.current.reset();
  });
  expect(result.current.values.text).toBe("Note One");
});
Sign up to request clarification or add additional context in comments.

Comments

1

You can simply cast it:

 act(() => {
   result.current.handleChange({
     target: {
       name: 'text',
       value: 'Note Two',
     },
   }) as React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>;
 });

Comments

0

I got into this same problem this was the fix.

// Create a mock type for the changeEvent
type MockFormEvent = ChangeEvent<HTMLInputElement | HTMLTextAreaElement>

  act(() => { result.current.handleChange({
  target: { 
    name: 'text',
    value: 'Note Two',
  }
 } as MockFormEvent);
});

This fixed it for me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.