【问题标题】:ts(2322): 'Dispatch<SetStateAction<string>>' is not assignable to type '(value: OptionValue) => void'ts(2322): 'Dispatch<SetStateAction<string>>' 不可分配给类型 '(value: OptionValue) => void'
【发布时间】:2021-07-22 14:41:40
【问题描述】:

我正在学习 Typescript 泛型与 React 集成,阅读 this article 并按照文章的代码进行操作,但出现此错误:

Type 'Dispatch<SetStateAction<string>>' is not assignable to type '(value: OptionValue) => void'.
  Types of parameters 'value' and 'value' are incompatible.
    Type 'OptionValue' is not assignable to type 'SetStateAction<string>'.
      Type 'number' is not assignable to type 'SetStateAction<string>'.ts(2322)
Select.tsx(15, 3): The expected type comes from property 'onChange' which is declared here on type 'IntrinsicAttributes & ISelectProps<OptionValue>'

这是我的全部代码:

import React, { useState } from 'react'
import Select from './Select'

const App: React.FC = () => {
  const targets = [
    { value: 'es3', label: 'ECMAScript 3' },
    { value: 'es5', label: 'ECMAScript 5' },
    { value: 'es2015', label: 'ECMAScript 2015' },
    { value: 'es2016', label: 'ECMAScript 2016' },
    { value: 'es2017', label: 'ECMAScript 2017' },
    { value: 'es2018', label: 'ECMAScript 2018' },
    { value: 'es2019', label: 'ECMAScript 2019' },
    { value: 2019, label: 'ECMAScript 2019' },
  ]

  const [target, setTarget] = useState('es2019')

  return (
    <>
      {/* <Select value={target} onChange={(value) => setTarget(value)} /> */}
      <Select options={targets} value={target} onChange={setTarget} />
    </>
  )
}

export default App

在这个Select 组件中使用泛型

/* eslint-disable react/destructuring-assignment */
import React, { useCallback } from 'react'

export type OptionValue = string | number
export type Option<T extends OptionValue> = {
  value: T
  label: string
}

interface ISelectProps<T extends OptionValue> {
  options: Option<T>[]
  value: T
  onChange: (value: T) => void
}

const Select = <T extends OptionValue>(props: ISelectProps<T>) => {
  const { options, onChange } = props
  const handleOnChange = useCallback((e: React.FormEvent<HTMLSelectElement>) => {
    const { selectedIndex } = e.currentTarget
    const selectedOption = options[selectedIndex]
    onChange(selectedOption.value)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return (
    // eslint-disable-next-line unicorn/consistent-destructuring
    <select value={props.value} onChange={handleOnChange}>
      {options.map((option) => (
        <option key={option.value} value={option.value}>
          {option.label}
        </option>
      ))}
    </select>
  )
}

export default React.memo(Select)

codesandbox 是here

我知道我可以将OptionValue 类型显式传递给useState 来解决这个问题,如下所示:

const [target, setTarget] = useState<OptionValue>('es2019')

但是我发现这个article没有这样做,而且这种方式不是打字稿通用的,对吧?

【问题讨论】:

    标签: reactjs typescript tsx


    【解决方案1】:

    在您引用的文章中,targets 中的所有values 都是stringnumber。在您的代码中,valuestargets 包含两者。

    当你打电话时

    useState('es2019')
    

    我认为 Typescript 推断 targetstring 类型(它不能推断这也可能是 number)。

    所以

    const [target, setTarget] = useState<OptionValue>('es2019')
    

    你基本上是在告诉 typescript:我想用 string 初始化它,但它也可以是一个数字。

    一个奇怪的解决方法也是:

    const [target, setTarget] = useState(targets[6].value);
    

    【讨论】:

    • const [target, setTarget] = useState(targets[6].value); 这个好像不行
    • @Liam_1998 使用const [target, setTarget] = useState&lt;string | number&gt;("es2019"); 作为(也)此答案中的建议。
    【解决方案2】:

    这篇文章没有这个因为targets中的value总是有类型是stringnumber。所以他可以在没有OptionValue的情况下声明状态target,只需要target的类型与value的类型相同。

    在您的情况下,targets 中的 value 具有 stringnumber 两种类型。所以在声明状态target的时候,需要加上类型OptionValue

    【讨论】:

    • 但是我尝试删除target{ value: 2019, label: 'ECMAScript 2019' }的最后一项,它仍然提醒我同样的错误。 :-(
    猜你喜欢
    • 2020-07-05
    • 1970-01-01
    • 2021-12-05
    • 2021-12-10
    • 2023-02-13
    • 2023-01-12
    • 1970-01-01
    • 2021-07-14
    • 2021-01-12
    相关资源
    最近更新 更多