3

I'm trying to create a custom "Input" component and extend the default html input attributes (props).

However, I am also trying to override the default 'size' attribute using Typescript's Omit within my own interface definitions, but can't seem to get it to work.

(I've also tried Typescript's Pick/Exclude and a custom Override type, but get same error...)

Similar threads I've already tried:

import React from 'react';

type A = React.HTMLAttributes<HTMLInputElement>;

interface InputProps extends Omit<A, 'size'> {
  size?: 'sm' | 'md' | 'lg';
}

const Input = (props: InputProps) => {
  return <input {...props} />;
};

TS Error

(property) JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
Type '{ size?: "sm" | "md" | "lg"; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; suppressContentEditableWarning?: boolean; ... 250 more ...; onTransitionEndCapture?: (event: TransitionEvent<...>) => void; }' is not assignable to type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.
  Type '{ size?: "sm" | "md" | "lg"; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; suppressContentEditableWarning?: boolean; ... 250 more ...; onTransitionEndCapture?: (event: TransitionEvent<...>) => void; }' is not assignable to type 'InputHTMLAttributes<HTMLInputElement>'.
    Types of property 'size' are incompatible.
      Type 'string' is not assignable to type 'number'.
        Type 'string' is not assignable to type 'number'.ts(2322)
TrieuNomad
  • 1,651
  • 1
  • 21
  • 24

3 Answers3

6

Replace interface InputProps.... with

type InputProps = {
 size?: 'sm' | 'md' | 'lg';
} & Omit<A, 'size'>;

Based on OP's comment,

const Input = ({ size, ...props }: InputProps) => {
  // if(size) do something...
  return <input {...props} />;
};
SILENT
  • 3,916
  • 3
  • 38
  • 57
  • Hmm... the error still persists even with this definition – TrieuNomad Oct 09 '20 at 15:57
  • @TrieuNomad Its probably complaining about mismatching with the input since you are passing all props directly into input. Try out my new suggestion. – SILENT Oct 09 '20 at 16:55
  • I ended up going with but your way works too. Will accept this as resolution since it fixes the ts error. – TrieuNomad Oct 09 '20 at 17:25
  • 1. Even when the `size` prop is not directly passed to the `input` element, we still get the same error. 2. When we export the type `InputProps` (to use it in another file), it gives the same error. What could be wrong here? – Srishti Gupta Oct 06 '22 at 06:14
2

The design flaw here isn’t the props that you’re accepting into your custom Input component. It is that you are are then passing those same props down to the HTML input component, even though you already know that your size property differs from the expected size property. That is what is reflected in the error that you’ve pasted here.

You do need to fix your type definition as explained by @silent.

But after doing that, you need to do something inside your Input component to map your custom size strings into numbers before you can pass them down to input.

Edit:

Based on your comment you have said that these are CSS class names. So they need to be passed down to the input component in the className property, not the size property.

// sets default size to 'md' - you can change or remove this
const Input = ({size = 'md', className, ...props}: InputProps) => {
    // combine with existing class names
    const newClass = className + ' ' + size; // could trim redundant spaces but don't need to

    return (
        <input 
            {...props} 
            className={newClass}
        />;
};

You don't need to pass anything down to size. In fact this component cannot ever set anything to the size property on the input because we took size out when we used the spread operator ({size = 'md', className, ...props}). ...props is a "rest" parameter meaning that it has all the properties not otherwise extracted, so it will not include the size or className from the InputProps.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
  • I don't really want to map the strings into designated number values, I just want to apply css classes. However I do get what you're saying, and I guess I could pass size={undefined} to the nested html input, which gets rid of the error. Thanks for the explanation! – TrieuNomad Oct 09 '20 at 16:02
  • Well in that case pass them down to the `className` property. – Linda Paiste Oct 09 '20 at 18:53
1

You don't have to replace your interface.

For example

export interface InputProps extends Omit<A,'size'> {
 size?: InputSize;
 /* other properties */
}

type A = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>,HTMLInputElement>;
type InputSize = 'xs' | 'sm' | 'md' | 'lg';

const Input = (props: InputProps) => {
 const { style, className, size = 'md', ...rest } = props;
 /* you don't have to pass a default value to size */

 return <input {...rest} />;
};
ffcabbar
  • 351
  • 1
  • 3
  • 18