在TypeScript中扩展react-select接口属性

7

我为我的React项目自定义了一个react-select下拉组件。当我尝试使用React.HTMLAttributes<HTMLSelectElement | HTMLInputElement>扩展接口时,它显示“未找到符合此调用的重载”。

我尝试扩展不同的属性以获取默认值,如id、label、name、placeholder等。如何在props内获取默认属性?有人遇到过类似的问题吗?

示例代码:

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;

使用上述代码后,我无法获取 props.id 或 props.name 等属性。

我在这里创建了一个沙盒:https://codesandbox.io/s/eloquent-cohen-l8e42?file=/src/App.tsx 你的代码似乎是可以工作的。我唯一遇到的问题是接口中缺少"label"。你能否在沙盒中重现你的问题? - JAM
谢谢JAM。 实际上,我们需要扩展react-select而不是普通的javascript select。此外,我们需要默认获取id、name和标签,而无需将它们添加到界面中。就像[https://codesandbox.io/s/naughty-paper-xgmbe?file=/src/App.js]这个例子一样,我可以在不添加到界面中的情况下编写标签。 - Ebin
4个回答

8

编辑:

React-Select v5现在原生支持TypeScript,因此进行了一些类型更改(详情)。

这是一个更新的v5示例(react-select/async),类似于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

原始答案:

这在官方react-select文档中有记录。

引用:

包装Select组件 通常情况下,Select组件被包装在另一个组件中,该组件在整个应用程序中使用,并且包装器应该与原始Select组件一样灵活(即允许不同的选项类型、组、单选或多选)。为了提供这种灵活性,包装组件应重新声明泛型并将其转发到底层的Select。以下是如何执行此操作的示例:

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 })} />
  );
}

它还解释了如何使用附加自定义props扩展props的方法:

您可以使用模块增强来向Select prop类型添加自定义props:

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

但是我个人更喜欢使用&字符添加自定义界面,并使用自定义界面添加自定义属性(例如:ReactSelectAsync示例,请参见... & 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} />
  );
}

在尝试您的解决方案时,发现TypeScript在使用自定义选择组件时抱怨未知类型。您是否从react-select以外的其他地方导入了基本类型? - Jason Bert
1
原来我的编辑器添加了 import { Props } from "react-select/dist/declarations/src/Select" 而不是 import { Props } from "react-select",这就是 TypeScript 抱怨的原因。 - Jason Bert
我不得不给你的回答点个踩,因为在当前版本的react-select中,OptionTypeBaseGroupTypeBase已经不存在了。 - Jason Bert
1
@JasonBert 是的,React-Select v5 已经被重写为 TypeScript,因此类型略有变化。我会更新关于 v5 的答案。 - josias
@JasonBert 答案已更新 :) - josias

2
您可以这样使用:
import React from 'react'
import { omit } from 'lodash';
import Select, { GroupBase, Props } from 'react-select';

type SelectProps<Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>> = Props<Option, IsMulti, Group> & {
    label?: string;
    id?: string;
}

const SearchableSelect = <
    Option,
    IsMulti extends boolean = false,
    Group extends GroupBase<Option> = GroupBase<Option>
>(props: SelectProps<Option, IsMulti, Group>) => {
    const id = props.id || Math.random().toString()
    const reactSelectProps = omit(props, ['label', 'className'])

    return (
        <div>
            {props.label && (
                <label htmlFor={id} className="block text-sm font-medium text-gray-700">
                    {props.label}
                </label>
            )}
            <Select
                {...reactSelectProps}
                theme={(theme) => ({
                    ...theme,
                    colors: {
                        ...theme.colors,
                        primary: "var(--primary-500)",
                    },
                })}
            />
        </div>
    );
}


export default SearchableSelect


你的答案可以通过添加更多支持信息来改进。请[编辑]以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案正确无误。你可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

1

即使在阅读了这里的一些答案后,我仍然对这种方法感到困惑。

所以这是我的完整方法:

首先,ReactSelect没有类似于扩展Html按钮的props的清晰接口,例如:Interface IbuttonProps extends HTMLButtonElement

react-select props默认props具有接口名称Props,可以从以下两个路径中导入:

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

只有后者的import { Props } from "react-select"是正确的

Props接口也使用泛型,因此它看起来类似于这样:

Props<Option,IsMulti,Group>

为了扩展这个接口,我们将使用带有类型参数默认值的通用类型定义,如下所示:

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

因此,当我们创建自己的 react-select Props 扩展时,它将看起来像这样:

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

结果:


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}/>)
}

-4
您可以直接从 react-select 包中导入 Props 类型。您可能希望扩展它,以便要求 labelid 均已定义。
import React from "react";
import ReactSelect, { Props } from "react-select";

type SelectProps = Props & {
  id: string;
  label: string;
};

const Select: React.FC<SelectProps> = (props) => {
  return (
    <div>
      <label htmlFor={props.id}>{props.label}</label>
      <div>
        <ReactSelect {...props} />
      </div>
    </div>
  );
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接