Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

To extend the react-select interface property in typescript

I have customize a react-select drop-down component for my react-project.When i try to extend the interface using React.HTMLAttributes<HTMLSelectElement | HTMLInputElement>. It shows "No overload matches this call". I have tried to extend different attributes to get the default values like id, label, name, placeholder, etc. How can I get the default properties inside the props? Did anyone experience any similar issue?

sample code:

import * as React from "react";

import ReactSelect from 'react-select';

export interface SelectProps extends React.HTMLAttributes<HTMLSelectElement | HTMLInputElement> {
    options: Array<any>;
    isMulti: boolean;
    isDisabled: boolean;

};

const Select: React.FC<SelectProps> = (props: SelectProps) => {

    return (<div>
        <label htmlFor={props.id}>{props.label}</label>
        <div>
            <ReactSelect
                {...props}
            />
        </div>
    </div>)
}
export default Select;

After using the above code, I am not able to get props.id or props.name, etc.

like image 820
Ebin Avatar asked Mar 27 '26 12:03

Ebin


2 Answers

Edit:

React-Select v5 now natively supports TypeScript, therefore some type changes have been made (details).

Here's an updated example for v5 (react-select/async), similar to the original one with v4:

import ReactSelectAsync, { AsyncProps } from "react-select/async";
import { GroupBase } from "react-select";

interface CustomSelectAsyncProps {
  additionalCustomProp: number;
}

function SelectAsync<
  OptionType,
  IsMulti extends boolean = false,
  GroupType extends GroupBase<OptionType> = GroupBase<OptionType>
>({
  additionalCustomProp,
  ...props
}: AsyncProps<OptionType, IsMulti, GroupType> & CustomSelectAsyncProps) {
  return <ReactSelectAsync {...props} />;
}

export default SelectAsync;

Codesandbox

Original Answer:

This is documented on the official react-select documentation.

Quote:

Wrapping the Select component Oftentimes the Select component is wrapped in another component that is used throughout an app and the wrapper should be just as flexible as the original Select component (i.e., allow for different Option types, groups, single-select, or multi-select). In order to provide this flexibility, the wrapping component should re-declare the generics and forward them to the underlying Select. Here is an example of how to do that:

function CustomSelect<
  Option,
  IsMulti extends boolean = false,
  Group extends GroupBase<Option> = GroupBase<Option>
>(props: Props<Option, IsMulti, Group>) {
  return (
    <Select {...props} theme={(theme) => ({ ...theme, borderRadius: 0 })} />
  );
}

It also explains how to extend the props with additional custom props:

You can use module augmentation to add custom props to the Select prop types:

declare module 'react-select/dist/declarations/src/Select' {
  export interface Props<
    Option,
    IsMulti extends boolean,
    Group extends GroupBase<Option>
  > {
    myCustomProp: string;
  }
}

But I personally prefer to just add a custom interface using the & character with a custom interface to add custom props (example with ReactSelectAsync, see ... & CustomSelectAsyncProps):

interface CustomSelectAsyncProps {
  additionalCustomProp: number;
}

function SelectAsync<
  OptionType extends OptionTypeBase,
  IsMulti extends boolean = false,
  GroupType extends GroupTypeBase<OptionType> = GroupTypeBase<OptionType>
>({
  additionalCustomProp,
  ...props
}: Props<OptionType, IsMulti, GroupType> & CustomSelectAsyncProps) {
  return (
    <ReactSelectAsync {...props} />
  );
}
like image 81
josias Avatar answered Mar 30 '26 02:03

josias


I was struggling with this approach even after reading some of the answers here.

So here is my full approach:

First of all, ReactSelect does not have a clean interface for their props similar to how you'd extend the Html button, example: Interface IbuttonProps extends HTMLButtonElement

The react-select props default props has the interface name Props which can be imported from both of these paths:

import { Props } from "react-select/dist/declarations/src/Select" instead import { Props } from "react-select"

only the latter import { Props } from "react-select" is correct!

The Props interface also uses generics so it will look similar to this:

Props<Option,IsMulti,Group>

To extend this interface we'll use generic type definitions with type parameter defaults like this:

type MyCustomSelectProps<Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>> = Props<
   Option,
   IsMulti,
   Group
>

So when we create our own extension of the react-select Props it will look something like this:

type MyCustomSelectProps<Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>> = Props<
    Option,
    IsMulti,
    Group
> & {
 MyCustomProperty:string;
 MyOtherCustomProperty:number;
};

Result:


import * as Select from 'react-select';
import { GroupBase } from 'react-select';
import { Props } from 'react-select';


type SelectProps<Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>> = Props<
    Option,
    IsMulti,
    Group
> & {
    MyCustomProperty:string;
    MyOtherCustomProperty:number;
};


export const Select = <Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>(
    props: SelectProps<Option, IsMulti, Group>,
) => {
    return (<Select {...props}/>)
}
like image 37
Stanley Avatar answered Mar 30 '26 01:03

Stanley