首页 > 解决方案 > 如何让 TypeScript 知道我正在使用哪些 proptypes?

问题描述

如何让 TypeScript 知道组件正在接收哪些道具类型?例如,如果multiple是真的,我希望 TypeScript 期望这selectValue是一个字符串数组,或者如果它不存在,那么 TypeScript 应该期望selectValue是一个字符串。

我真的不喜欢必须将我的道具转换为合适的类型才能让 TypeScript 开心。

这是我的自定义<Select>组件的当前代码:

import React, { ReactNode } from 'react';
import MultipleSelect from './MultipleSelect';
import SingleSelect from './SingleSelect';

interface MultipleSelectProps {
  selectValue: string[];
  multiple: true;
  onSelectChange: (values: string[]) => void;
  renderValue?: (value: string[]) => React.ReactNode;
}

interface SingleSelectProps {
  selectValue: string;
  onSelectChange: (values: string) => void;
  multiple?: never;
  renderValue?: (value: string) => React.ReactNode;
}

type SelectProps = SingleSelectProps | MultipleSelectProps;

const Select: React.FC<SelectProps> = ({
  selectValue,
  multiple,
  onSelectChange,
  renderValue,
  children,
}) => {
  if (multiple) {
    const multipleSelectValue = selectValue as string[];
    const multipleSelectChange = onSelectChange as (value: string[]) => void;
    const multipleRenderValue = renderValue as (value: string[]) => ReactNode;

    return (
      <MultipleSelect
        selectValue={multipleSelectValue}
        onSelectChange={multipleSelectChange}
        renderValue={multipleRenderValue}
      >
        {children}
      </MultipleSelect>
    );
  }

  const singleSelectValue = selectValue as string;
  const singleSelectChange = onSelectChange as (value: string) => void;
  const singleRenderValue = renderValue as (value: string) => ReactNode;

  return (
    <SingleSelect
      selectValue={singleSelectValue}
      onSelectChange={singleSelectChange}
      renderValue={singleRenderValue}
    >
      {children}
    </SingleSelect>
  );
};
export default Select;

我想改为这样写:

import React, { ReactNode } from 'react';
import MultipleSelect from './MultipleSelect';
import SingleSelect from './SingleSelect';

interface MultipleSelectProps {
  selectValue: string[];
  multiple: true;
  onSelectChange: (values: string[]) => void;
  renderValue?: (value: string[]) => React.ReactNode;
}

interface SingleSelectProps {
  selectValue: string;
  onSelectChange: (values: string) => void;
  multiple?: never;
  renderValue?: (value: string) => React.ReactNode;
}

type SelectProps = SingleSelectProps | MultipleSelectProps;

const Select: React.FC<SelectProps> = ({
  selectValue,
  multiple,
  onSelectChange,
  renderValue,
  children,
}) => {
  return (
{
  multiple ?
    <MultipleSelect
      selectValue={selectValue}
      onSelectChange={onSelectChange}
      renderValue={renderValue}
    >
      {children}
    </MultipleSelect>
    :
    <SingleSelect
      selectValue={selectValue}
      onSelectChange={onSelectChange}
      renderValue={renderValue}
    >
      {children}
    </SingleSelect>
  );
};
export default Select;

我希望能够像使用原生 html 一样使用我的组件<select>。像这样:

<Select selectValue={selectValue} onSelectChange={onSelectChange} multiple>
  {options}
</Select>

标签: reactjstypescript

解决方案


我就是这样做的。通过定义类型,TypeScript 可以知道 selectValue 的类型和其他可能会改变的类型。

import React, { ReactNode } from 'react';
import MultipleSelect from './MultipleSelect';
import SingleSelect from './SingleSelect';


type SelectProps = {
    kind: 'multiple',
    selectValue: string[];
    onSelectChange: (values: string[]) => void;
    renderValue?: (value: string[]) => React.ReactNode;
} | {
    kind: 'single',
    selectValue: string;
    onSelectChange: (values: string) => void;
    renderValue?: (value: string) => React.ReactNode;

}

const Selector = (props: SelectProps, children): JSX.Element => {
  return (
    <>
        {
        props.kind === 'multiple' 
        ?
            <MultipleSelect
            selectValue={props.selectValue}
            onSelectChange={props.onSelectChange}
            renderValue={props.renderValue}
            >
            {children}
            </MultipleSelect>
        :
            <SingleSelect
            selectValue={props.selectValue}
            onSelectChange={props.onSelectChange}
            renderValue={props.renderValue}
            >
            {children}
            </SingleSelect>
        
        }
    </>
  )
}

export default Selector

推荐阅读