类型 'number[]' 不能分配给类型 '[ReactText, ReactText]'

Бог*_*дан 0 typescript reactjs typescript-typings

我正在使用弹性 UI 组件双范围:

const [yearRangeVal, setYearRangeVal] = useState([1974, 2007]);
  
const onYearRangeChange = (value: <number[]>) => {
    setYearRangeVal(value);
  };

      <EuiDualRange
        min={1974}
        max={2007}
        showTicks
        tickInterval={10}
        onChange={onYearRangeChange}
        value={yearRangeVal}
      />
Run Code Online (Sandbox Code Playgroud)

并在 VSCode 中收到此错误:

没有重载匹配这个调用。重载 2 of 2, '(props: EuiDualRangeProps, context: any): EuiDualRange',出现以下错误。类型 '(value: <number[]>) => void' 不能分配给类型 '(values: [ReactText, ReactText], isValid: boolean, event: ChangeEvent | MouseEvent<HTMLButtonElement, MouseEvent> | KeyboardEvent<... >) => 无效'。重载 2 of 2, '(props: EuiDualRangeProps, context: any): EuiDualRange',出现以下错误。类型 'number[]' 不能分配给类型 '[ReactText, ReactText]'。

此代码来自 Elastic UI 库(请参阅 ValueMember 变量):

type ValueMember = number | string;
    export interface EuiDualRangeProps extends Omit<EuiRangeSliderProps, 'onChange' | 'onBlur' | 'onFocus' | 'value'> {
        value: [ValueMember, ValueMember];
        onBlur?: (event: React.FocusEvent<HTMLInputElement> | React.FocusEvent<HTMLDivElement>) => void;
        onFocus?: (event: React.FocusEvent<HTMLInputElement> | React.FocusEvent<HTMLDivElement>) => void;
        onChange: (values: [ValueMember, ValueMember], isValid: boolean, event: React.ChangeEvent<HTMLInputElement> | React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLInputElement>) => void;
    
Run Code Online (Sandbox Code Playgroud)

只是无法理解什么是 ReactText((JSX 属性)值:[React.ReactText, React.ReactText])以及为什么我的类型不正常。

Lin*_*ste 5

ReactText是 的别名string | number。它定义了哪些类型可以在 React 中打印为文本,包括字符串和数字。

EuiDualRange组件说它的值必须始终是一个包含两个元素的数组,也就是一个Tuple 类型。即使您的state数组仅使用两个值进行初始化[1974, 2007],打字稿也假定它可以是任意长度,并允许您setState使用number任意长度的数组。换句话说, for 的推断类型useStatenumber[]但我们想将其限制为仅[number, number]

当您手动设置useStatewith的泛型时,useState<[number, number]>([1974, 2007])属性上的错误value就会消失。

但是我们仍然会在 上出错onChange,即使我们设置了(value: [number, number])。这是因为EuiDualRange可以接受stringANDnumber并且其onChange回调的打字稿定义说它返回的值可能是stringsor numbers,因此它希望您的回调接受两者。

确保您的回调接受正确类型的一种方法是从包中导入定义,这是针对整个函数而不是道具,并将其应用于您的回调。

import {EuiDualRange, EuiDualRangeProps} from "@elastic/eui";
Run Code Online (Sandbox Code Playgroud)
const onYearRangeChange: EuiDualRangeProps['onChange'] = (value) => { /** ... **/ }
Run Code Online (Sandbox Code Playgroud)

如果我们这样做,我们的回调现在接受正确类型的值,但是在setState使用该值调用时我们会进一步出错,因为值类型比我们的状态类型更广泛。

有很多方法可以解决这个问题。

  1. 如果我们可以根据包的行为确信组件将始终onChange使用与我们提供的相同的类型调用,value那么我们基本上可以告诉打字稿“忽略错误,因为我知道的比你多”。你会写
  const onYearRangeChange: EuiDualRangeProps['onChange'] = (value) => {
    setYearRangeVal(value as [number, number]);
  };
Run Code Online (Sandbox Code Playgroud)

其中“as”关键字用于细化类型(类型断言)。这是最简单的解决方案。有些人会避免这种策略,因为如果事实证明自己的主张是错误的,那么您就会面临潜在的错误。

根据我从EuiDualRange组件中看到的内容,它实际上是在返回数字,所以我认为在这种情况下使用“as”是可以的。但我想包括一个替代方案,因为这可能不是“最佳实践”。

  1. 您可以通过检查返回值并确保它们实际上是numbers而不是strings类型保护)来缩小类型。这意味着您的代码在运行时做了额外的工作(尽管它是微不足道的),但您可以绝对确定您的类型是正确的。在类型错误的情况下,您可以什么都不做,否则您可能会抛出错误。在这个例子中,为了可读性,我将元组解构为它的各个元素。
  const onYearRangeChange: EuiDualRangeProps['onChange'] = ([min, max]) => {
    if ( typeof min === "number" && typeof max === "number") {
      setYearRangeVal([min, max]);
    }
    // optional else { }
  };
Run Code Online (Sandbox Code Playgroud)
  1. 如果年份是数字还是字符串对您的代码不重要,您可以允许您useState同时接受数字和字符串。
const [yearRangeVal, setYearRangeVal] = useState<[ReactText, ReactText]>([1974, 2007]);
Run Code Online (Sandbox Code Playgroud)