简体   繁体   English

如何为 event.target.value 设置类型?

[英]How to set type for event.target.value?

I have this Select component:我有这个 Select 组件:

type SelectProps = {
  title: string;
  name: string;
  items: string[];
  onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
  defValue: string;
};

const Select: FC<SelectProps> = ({ title, name, items, onChange, defValue }) => {
  return (
    <>
      <label htmlFor={name}>
        {title}:
      </label>
      <select name={name} onChange={onChange} defaultValue={defValue}>
        {items.map((item) => (
          <option value={item} key={item}>
            {item}
          </option>
        ))}
      </select>
    </>
  );
};

and I'm handling onChange with this function:我正在用这个 function 处理 onChange:

const onThemeChange = (e: ChangeEvent<HTMLSelectElement>) => {
  const theme = e.target.value;
  setTheme(theme)
};

...

<Select
  title='Theme'
  defValue={props.theme}
  name='theme'
  items={['light', 'dark']}
  onChange={onThemeChange}
/>

My setTheme action creator accepts argument with type 'light' | 'dark'我的setTheme动作创建者接受类型为'light' | 'dark' 'light' | 'dark' , so I'm getting an error: 'light' | 'dark' ,所以我收到一个错误:

Argument of type 'string' is not assignable to parameter of type '"light" | "dark"'

What is the best way to solve this issue?解决此问题的最佳方法是什么?

There is away, but it requires a little trick.有距离,但它需要一个小技巧。

First, let's recognize the relationships between types in your SelectProps :首先,让我们认识一下SelectProps中类型之间的关系:

  • the items are string literals这些items是字符串文字
  • in onChange , the event will have a target.value equal to one of your itemsonChange中,该事件的target.value等于您的items之一
  • the defValue should also be one of the items defValue也应该是其中items

To express these constraints, we need to use a generic interface.为了表达这些约束,我们需要使用通用接口。

type SelectProps<T extends string> = {
  title: string;
  name: string;
  items: T[];
  onChange: (e: ChangeEvent<HTMLSelectElement> & { target: { value: T }}) => void;
  defValue: DeferTypeInference<T>;
};

const Select = function<T extends string>({ title, name, items, onChange, defValue }: SelectProps<T>) {
  return (
    <>
      <label htmlFor={name}>
        {title}:
      </label>
      <select name={name} onChange={onChange} defaultValue={defValue}>
        {items.map((item) => (
          <option value={item} key={item}>
            {item}
          </option>
        ))}
      </select>
    </>
  );
};

We have achieved everything.我们已经取得了一切。

<Select
  title='Theme'
  defValue="light" // only "light" or "dark" are accepted
  name='theme'
  items={['light', 'dark']}
  onChange={event => event.target.value} // event.target.value is "light" or "dark"
/>

Note the use of a type called DeferTypeInference<T> .请注意使用名为DeferTypeInference<T>的类型。 If you're curious why it's there, check out this answer .如果您好奇它为什么会出现,请查看此答案

The quickest way of doing so would be to do type assertions.最快的方法是进行类型断言。

Assuming that this is how you initialised the state,假设这是您初始化 state 的方式,

type ThemeState = 'light' | 'dark';

const [theme, useTheme] = useState<ThemeState>('light');

And then, on your onThemeChange method, you will assert the value as ThemeState然后,在您的onThemeChange方法上,您将value断言为ThemeState

const theme = e.target.value as ThemeState;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM