代码之家  ›  专栏  ›  技术社区  ›  AmProsius

如何在React with TypeScript中的onChange方法中正确设置值的类型?

  •  0
  • AmProsius  · 技术社区  · 2 年前

    我使用React Aria Components RadioGroup 具有 controlled values 以显示选项阵列。键入选项时使用 enum 。要在本地存储所选选项,我使用 useState 第一个选项作为默认值。

    不管怎样 Argument of type 'string' is not assignable to parameter of type 'SetStateAction<Option>'. 使用此代码:

    import { useState } from "react";
    import { Label, Radio, RadioGroup } from "react-aria-components";
    
    enum Option {
      ONE = "option_one",
      TWO = "option_two",
    }
    
    export default function App() {
      const options: Option[] = [Option.ONE, Option.TWO];
      const [selectedOption, setSelectedOption] = useState(options[0]);
    
      return (
        <RadioGroup
          onChange={(value) => setSelectedOption(value)}
          value={selectedOption}
        >
          <Label>Options</Label>
          {options.map((option) => (
            <Radio key={option} value={option}>
              {option}
            </Radio>
          ))}
        </RadioGroup>
      );
    }
    

    这个 使用状态 id正确推断为 Option ,但是 value 被视为 string .

    Minimum reproduction sandbox

    到目前为止,唯一有效的方法似乎是一个类型断言,如:

    export default function App() {
      // ...
    
      return (
        <RadioGroup
          onChange={(value) => setSelectedOption(value as Option)}
          value={selectedOption}
        >
          // ...
        </RadioGroup>
      );
    }
    

    我通常会尽量避免类型断言。有没有其他更流畅的方法来获得合适的类型 onChange 事件

    2 回复  |  直到 2 年前
        1
  •  0
  •   T.J. Crowder    2 年前

    因为你是从字符串开始的( value ),你必须告诉TypeScript你知道它是有效的 Option 。至少有两种方法:

    1. 使用 type predicate 或者 assertion function .

    2. 使用类型断言。

    我更喜欢使用类型谓词或断言函数,因为它不仅满足TypeScript,还可以在运行时检查内容,这样,如果我犯了错误(例如:向不在中的列表添加硬编码的“none”选项 选项 ),我得到了一个可以修复的明显错误。在这里,您可能需要一个断言函数,因为没有一个有效的代码路径可以使值不正确:

    function assertIsOption(value: string): asserts value is Option {
        if (!Object.keys(Option).includes(value)) {
            throw new Error(`Invalid Option value: "${value}"`);
        }
    }
    

    (有多种方法来解释这一点;重点是它是一个断言函数,用于检查 价值 .)

    然后在您的点击处理程序中使用它:

    <RadioGroup onChange={(value) => {
        assertIsOption(value);
        setSelectedOption(value);
    }} value={selectedOption}>
    

    Playground example (注意:这需要一个 长的 操场完成加载libs的时间)。

        2
  •  0
  •   Nicholas Tower    2 年前

    我和你一样避免使用类型断言,但在这种情况下我会使用类型断言。上的类型信息 <RadioGroup> 无法检查子元素并分析传递给的值 <Radio> 元素。然而,您知道typescript所不知道的事情:您传递给子级的所有值都在该枚举中,这会影响无线电组在其事件中可以提供的值。您可以使用类型断言来传达这些额外的知识。

    这确实会带来一些小风险,主要是在开发过程中。如果有人更改了值,则断言将不再正确。如果您愿意,您可以编写代码在运行时检查值,但除非您的代码已被修改,否则检查永远不会失败,所以这对浏览器来说只是一项繁忙的工作。

    const valueIsOption = (value: string): value is Option {
      return Object.keys(Option).includes(value);
    }
    
    // ...
    onChange={(value) => {
      if (valueIsOption(value) {
        setSelectedOption(value)
      } else {
        console.error('fix your bug, dummy');
      }
    }
    

    捕捉这些开发时错误的另一个选项是保留断言,但编写一个单元测试,如果 <收音机> s收到一个非- Option 作为其价值。