reactjs - 如何输入 html select 元素,以便 event.target.value 引用选项值类型?

标签 reactjs typescript

我有一个名为 Select 的组件,它有 optionsvalueonChange 属性。 options 是具有标签和值属性的对象数组。 这是我的 Select.tsx 代码

import * as React from "react";
import type { ChangeEventHandler, ReactElement } from "react";

export interface OptionType {
  value: string | number | null;
  label: string;
}

export const Select = ({
  value,
  onChange,
  options
}: {
  value: string | number | null;
  onChange: ChangeEventHandler<HTMLSelectElement>;
  options: OptionType[];
}): ReactElement => (
  <select value={value ?? ""} onChange={onChange}>
    {options.map((option) => (
      <option value={option.value ?? ""} key={option.value}>
        {option.label}
      </option>
    ))}
  </select>
);

我像这样使用了 Select 组件,问题出在下面的 onChange 事件处理程序上。我希望 event.target.value 具有 UnitType 类型。

import * as React from "react";
import { Select } from "./Select";
const { useState } = React;

type UnitType = "cm" | "m" | "km";
interface UnitOptionType {
  label: string;
  value: UnitType;
}

// Select options array with a custom type
const timeOptions: UnitOptionType[] = [
  { label: "Centimeter", value: "cm" },
  { label: "Meter", value: "m" },
  { label: "Kilometre", value: "km" }
];

export const App = () => {
  const [value, setValue] = useState<UnitType | null>(null);

  return (
    <div>
      <Select
        options={timeOptions}
        value={value}
        onChange={(event) => {
          // I WANT this to have the type of UnitType
          // but it is string
          setValue(event.target.value)
        }}
      />
    </div>
  );
};

您可以在此 Codesandbox 链接上查看我的代码:

https://codesandbox.io/s/xenodochial-matan-i3p9z?file=/src/App.tsx:0-765

最佳答案

valueHTMLSelectElement 始终是 string 。它在 TypeScript 附带的 lib.dom 标准类型库中以这种方式键入。您无能为力改变这一点。


在本例中,我将使 <Select> 组件变得通用,以便它知道其选项的类型。

然后让 onChange 回调接收该类型,而不是整个事件。

最后,您将值转换为该类型,因为您的值将是选项之一的值。

把它们放在一起你会得到:

export interface OptionType<T extends string | number | null> {
  value: T;
  label: string;
}

export const Select = <T extends string | number | null>({
  value,
  onChange,
  options
}: {
  value: T;
  onChange: (newValue: T) => void;
  options: OptionType<T>[];
}): ReactElement => (
  <select value={value ?? ""} onChange={(event) => onChange(event.target.value as T)}>
    {options.map((option) => (
      <option value={option.value ?? ""} key={option.value}>
        {option.label}
      </option>
    ))}
  </select>
);

现在 onChange 提供 T ,这意味着选项值的并集,因此您可以将该值直接传递给 setState ,而无需通过事件对象进行代理。

      <Select
        options={timeOptions}
        value={value}
        onChange={setValue}
      />

Typescript playground with the above code and no type errors.


最后一点:您使用值的类型为 string | number | null ,但如上所述, .value 将始终返回一个字符串,因此您永远不会从中得到 nullnumber 。因此,支持这些类型将会更加复杂,并且您无法单独在 typeland 中完成它,因为您需要知道如何在运行时将 string 转换为您想要的类型。

关于reactjs - 如何输入 html select 元素,以便 event.target.value 引用选项值类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68896793/

相关文章:

javascript - 在 React js 中做一次,然后每 15 秒做一次

reactjs - 如何在 Jest 中测试模拟函数的返回值

javascript - 如何在刷新浏览器时将应用程序重定向到主页?

reactjs - Webpack:是否可以仅在生产中隐藏源 map ?

visual-studio - 保存后文件从 VS 2015 解决方案中消失

angular - 如何修复 Angular 中的数据无法从服务传输到模块的问题

css - 在 Gatsby 中使用 ActiveClassName 和 CSS 模块向事件链接添加样式不起作用

javascript - TS2304 : Cannot find name '__decorate' , '__metadata' 和 '__extends'

angular - 模块没有导出成员

angular - subscribe 不是尝试在 Angular 2 中使用 JSON 文件时抛出的函数错误