skip to Main Content

If I have a generic Input component in a React Hook Form that can be used for both sign up & sign in forms, how can I make the IFormValues flexible, so that it can be used for both forms that have different inputs?
Sign Up has email, password and confirmPassword inputs, whereas Sign In only has email and password inputs.

interface IFormValues {
  email: string;
  password: string;
  confirmPassword: string;
}

type InputProps = {
  hookValue: Path<IFormValues>;
  register: UseFormRegister<IFormValues>;
  id: string;
  ...etc
};

const Input = ({ hookValue, id, ...etc }: InputProps) => 
  <StyledInput {...register(hookValue)} id={id} ...etc />

Would it be a union type like this? (or is there a way to pass in the correct type being used to the input component?)

interface ISignIn {
  email: string;
  password: string;
}

interface ISignUp extends ISignIn {
  confirmPassword: string;
}

type InputProps = {
  hookValue: Path<ISignIn | ISignUp>;
  register: UseFormRegister<ISignIn | ISignUp>;
  id: string;
  ...etc
};

I tried implementing with the above, but this is producing the below TypeScript error for me in the call to the component:

<Input
  register={register} // this errors with the below output
  hookValue="email"
  label="Email"
/>
(property) register: UseFormRegister<ISignUp | ISignIn>
Type 'UseFormRegister<{ email: string; password: string; confirmPassword: string; }>' is not assignable to type 'UseFormRegister<ISignUp | ISignIn>'.
  Type 'ISignUp | ISignIn' is not assignable to type '{ email: string; password: string; confirmPassword: string; }'.
    Property 'confirmPassword' is missing in type 'ISignIn' but required in type '{ email: string; password: string; confirmPassword: string; }'.ts(2322)
input.component.tsx(24, 3): The expected type comes from property 'register' which is declared here on type 'IntrinsicAttributes & InputProps'

2

Answers


  1. Chosen as BEST ANSWER

    I managed to get this working with the following, using extend which as I understand acts as a constraint on the generic - essentially making sure it's assignable to that type but keeping its stricter inferred value.

    export type ISignIn = {
      email: string;
      password: string;
    };
    
    export type ISignUp = {
      email: string;
      password: string;
      confirmPassword: string;
    };
    
    type InputProps<IFormValues extends ISignIn | ISignUp> = {
      hookValue: Path<IFormValues>;
      register: UseFormRegister<IFormValues>;
    };
    
    const Input = <IFormValues extends ISignIn | ISignUp>({
      hookValue,
      register,
    }: InputProps<IFormValues>) => {
    

  2. solution 1: use & operator like ISignIn & ISignUp

    solution 2: typeof-type-guards

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search