我正在学习 Typescript generic 与 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)
代码沙盒是 here
我知道我可以明确地将 OptionValue
类型传递给 useState
来解决这个问题:
const [target, setTarget] = useState<OptionValue>('es2019')
但是我发现这个 article 没有这样做,而且这种方式不是 typescript 通用的,对吧?
答案 0 :(得分:0)
在您引用的文章中,values
的所有 targets
要么是 string
,要么是 number
。在您的代码中,values
的 targets
包含两者。
当你打电话
useState('es2019')
我认为 Typescript 推断 target
是 string
类型(它不能推断这也可能是 number
)。
这样
const [target, setTarget] = useState<OptionValue>('es2019')
你基本上是在告诉打字稿:我想用 string
初始化它,但它也可以是一个数字。
一个奇怪的解决方法也是:
const [target, setTarget] = useState(targets[6].value);
答案 1 :(得分:0)
这篇文章没有这样做,因为value
中的targets
总是有类型是string
OR number
。所以他可以在没有target
的情况下声明状态OptionValue
,只需要target
的类型与value
的类型相同。
就您而言,value
中的 targets
具有 string
和 number
类型。所以当你声明状态target
时,你需要添加类型OptionValue