react-hook-form: issue: SubmitHandler type error in v7, probably due to UnpackNestedValue type change.

Version Number

7.20.5

Codesandbox

V6: https://codesandbox.io/s/reproduce-v6-j5qnfi?file=/src/DescriptionForm.tsx V7: https://codesandbox.io/s/reproduce-v7-1j4342?file=/src/DescriptionForm.tsx

Steps to reproduce

Description

At DescriptionForm.tsx, onSubmit has a TypeError on v7 (line 39), although it works fine on v6.15.8 (line 34). Below is the error message on v7.20.5

Argument of type '(formProps: DescriptionFormProps) => void' is not assignable to parameter of type 'SubmitHandler<DescriptionFormProps>'.
  Types of parameters 'formProps' and 'data' are incompatible.
    Type '{ content: string; startDate: { clone: {}; isValid: {}; year: {}; month: {}; date: {}; day: {}; hour: {}; minute: {}; second: {}; millisecond: {}; set: {}; get: {}; add: {}; subtract: {}; startOf: {}; endOf: {}; format: {}; ... 12 more ...; locale: {}; } | null; endDate: { ...; } | null; }' is not assignable to type 'DescriptionFormProps'.
      Types of property 'startDate' are incompatible.
        Type '{ clone: {}; isValid: {}; year: {}; month: {}; date: {}; day: {}; hour: {}; minute: {}; second: {}; millisecond: {}; set: {}; get: {}; add: {}; subtract: {}; startOf: {}; endOf: {}; format: {}; diff: {}; valueOf: {}; unix: {}; ... 9 more ...; locale: {}; } | null' is not assignable to type 'MaterialUiPickersDate'.
          Type '{ clone: {}; isValid: {}; year: {}; month: {}; date: {}; day: {}; hour: {}; minute: {}; second: {}; millisecond: {}; set: {}; get: {}; add: {}; subtract: {}; startOf: {}; endOf: {}; format: {}; diff: {}; valueOf: {}; unix: {}; ... 9 more ...; locale: {}; }' is not assignable to type 'MaterialUiPickersDate'.
            Types of property 'clone' are incompatible.
              Type '{}' is not assignable to type '() => Dayjs'.
                Type '{}' provides no match for the signature '(): Dayjs'.ts(2345)

Edit: I did try to provide the type SubmitHandler as the below answer suggests, but it just passes the same error up to NewDescriptionForm.

After an investigation, it seems that this is caused by the change in type UnpackNestedValue.

We could confirm that manually changing the type of UnpackNestedValue to the type in v6 gets rid of the SubmitHandler type error.

It seems that the future v8 is addressing this issue, but since I was migrating my system from v6 to v7, I wanted to open an issue here.

Please have a look. Thank you.

p.s. would you recommend that I wait for the v8 release?

v6

export declare type UnpackNestedValue<T> = T extends NestedValue<infer U> ? U : T extends Date | FileList ? T : T extends Record<string, unknown> ? {
    [K in keyof T]: UnpackNestedValue<T[K]>;
} : T;

v7

export declare type UnpackNestedValue<T> = T extends NestedValue<infer U> ? U : T extends Date | FileList | File ? T : T extends object ? {
    [K in keyof T]: UnpackNestedValue<T[K]>;
} : T;

Screenshots

<u>onSubmit Type on V6 & V7</u>

v6

CleanShot 2022-04-06 at 18 14 35@2x

v7

CleanShot 2022-04-06 at 18 14 49@2x

Expected behaviour

No type mismatch in SubmitHandler.

What browsers are you seeing the problem on?

Chrome

Relevant log output

No response

Code of Conduct

  • I agree to follow this project’s Code of Conduct

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 22 (11 by maintainers)

Most upvoted comments

The bottom line is still the fact that RHF tries to embrace the Web API as close as possible to leverage the performance optimizations that come with it, so it’s unadvised to use custom objects for a field’s value.

+ import type { SubmitHandler } from "react-hook-form"

  interface Props {
-  onSubmit: (formProps: DescriptionFormProps) => void;
+  onSubmit: SubmitHandler<DescriptionFormProps>;
  }

Got it. It seems that I can make v7 work if I change the defaultValue’s startDate and endDate types from dayjs() to string and just provide something like "2022-04-08.

The other thing why v6 “works” and not v7 is mostly due to the type of safety introduced on v7 which may be completely missing in v6.

@bluebill1049, I see that the OP’s issue is coming from that change. Is it by design to avoid the use of custom objects?

that’s correct. v7 is more strict than v6 in terms of type safety, at the same time it does bring some limitations. However, those issues are getting improved on v8.

@leochoo I would migrate to v8 if that’s something blocking you or else you may want to stay for v6. I don’t think we will bring many breaking changes into v8, but it’s not a promise as it’s still in the alpha version.

I sincerely appreciate your help.

Your TS issue with createDescription is

Type '{}' is not assignable to type '() => Dayjs'.

Yes, that is the same error that’s been circling around.

RHF types can’t evaluate that “complex” DayJS object correctly.

I see. In that case, what would be your recommendation? How could solve this in v7?

@bluebill1049, I see that the OP’s issue is coming from that change. Is it by design to avoid the use of custom objects?

The thing is, the issue seems to be gone in V8 (while alpha) V8 version of Codesandbox: https://codesandbox.io/s/reproduce-v8-6zfhih?file=/src/NewDescriptionForm.tsx

This makes my migration a bit more complicated. Not sure if I should migrate from v6 to v7 or not.

@Moshyfawn @bluebill1049 How would you approach using a custom object for a custom complex component like a WYSIWIG editor then?

We are using yup + a DraftJS editor, which generates an EditorState custom object that will need to be stored in the database. Example of our approach below.

const schema = yup.object({
    notes: yup.mixed<EditorState>(),
});

type FormValues = yup.InferType<typeof schema>;

const Form = (props) => {
  const form = useForm<FormValues>({
      defaultValues: props.initialValues,
      // yupResolver has type errors because EditorState is unpacked and the unpacked version is not equal to EditorState
      resolver: yupResolver(validationSchema)
  });

  // editorState is not of type EditorState, but some unpacked version of it
  const editorState = form.getValues().notes;
  
  return <form>
    {/* The name field allows any property of the custom object (more than 100, e.g. toJSON()) */}
    <TextEditor name="notes" control={form.control} /> 
  </form>;
};

Example of type from getValues():

Screenshot 2022-04-18 at 13 34 29

Update: Created a ticket to track the types issue #8214

No estimate at the moment, just working our way through. thanks @Moshyfawn’s help as usual.

@bluebill1049 Thank you for the advice. Any estimate on when v8 might be released? Also, I actually have another issue within that same Codesandbox so I will open the issue tomorrow.

@Moshyfawn Thanks again for your help.

Sorry, the original codebase is so complex that it is very hard for me to take out small parts and try to reproduce the error.

Yeah, I get that, no worries.

Your TS issue with createDescription is

Type '{}' is not assignable to type '() => Dayjs'.

It goes back to that RHF rule:

Avoid including custom object into the defaultValues. eg: moment, luxonas those will lead to unexpected result during internal object clone process.

RHF types can’t evaluate that “complex” DayJS object correctly.

But why did this work on RHF v6?

From V6 to V7, RHF became a bit more type-safe. Now, if previously the type was

T extends Record<string, unknown>

in V7 it’s

T extends object

@bluebill1049, I see that the OP’s issue is coming from that change. Is it by design to avoid the use of custom objects?

I mistakenly deleted this explanation so I added:

Ignore the error on ThunkAction. That part is omitted just for the sake of reproduction. The original codebase has no error.