1
votes

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)
2

2 Answers

2
votes

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} />;
};
1
votes

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.