'Typescript interface extends two component types

So this will output an <input> element - everything works perfectly:

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  error?: FieldError;
  icon?: string;
  id: string;
  register?: UseFormRegisterReturn;
}

const StyledInputComponent = styled.input`
...
`

const MyComponent = ({
  error,
  icon,
  id,
  register,
  onChange,
  ...props
}: InputProps) => {
  return (
    <StyledInputComponent
     hasError={!!error}
     id={id}
     onChange={onChange}
     placeholder={placeholder}
     type={type}
     {...register}
     {...props}
    />
  });
};

I now need to be able to have the consuming component choose between an <input> and a <textarea>.

The problem I have is that (I think) I need to extend the interface further like this:

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>, React.InputHTMLAttributes<HTMLTextAreaElement> {
  error?: FieldError;
  icon?: string;
  id: string;
  inputType?: "input" | "textarea";
  register?: UseFormRegisterReturn;
}

And I'm just using the new inputType prop to switch between outputted components:

{ inputType === "input" &&  
  <StyledInputComponent
   hasError={!!error}
   id={id}
   onChange={onChange}
   placeholder={placeholder}
   type={type}
   {...register}
   {...props}
  />
}
{ inputType === "textarea" &&  
 <StyledTextAreaComponent
  hasError={!!error}
  id={id}
  onChange={onChange}
  placeholder={placeholder}
  rows={4}
  {...register}
  {...props}
 />
}

However, trying to extend the interface for both an <input> and a <textarea> has lead to numerous errors, all along these lines: errors

What's the right way to go about resolving this?



Solution 1:[1]

I've tried your solution, but I have some problems when returning an input text or a textarea

function MyComponent<T extends 'input' | 'textarea'>(props: InputProps<T>) {
  const {inputType, ...rest} = props;

  if (inputType === "input") {
    return <input
//          ^error
      type={"text"}
      {...rest}
    />
  }

  if (inputType === "textarea") {  
    return <textarea
//          ^error
      {...rest}
    />
  }
  
  return null
}

This are the returned errors:

Type '{ type: "text"; } & Omit<InputProps<T>, "inputType">' is not assignable to type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.
  Type '{ type: "text"; } & Omit<InputProps<T>, "inputType">' is not assignable to type 'ClassAttributes<HTMLInputElement>'.
    Types of property 'ref' are incompatible.
      Type 'InputProps<T>["ref"] | undefined' is not assignable to type 'LegacyRef<HTMLInputElement> | undefined'.

playground with error

I can solve the problem if the rest variable is casted to JSX.IntrinsicElements["input"] or JSX.IntrinsicElements["textarea"]... but I don't think is the best solution... Any alternative?

playground solved casting the rest variable

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mtz