如何正确键入 define event.target.value?
How to properly type define event.target.value?
我正在创作一个节点包,但我的 typescript 定义有点问题。更具体地说 我发现 event.target.value
的定义超级混乱
问题描述:
我有以下事件处理程序:
import { ChangeEvent, useState } from 'react'
type FieldEvent = ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
export const useField = <T>(input: T) => {
const [value, setValue] = useState<T>(input)
const handleChange = (event: FieldEvent) => {
const { name, value: eventValue } = event.target
// @ts-expect-error
setValue(eventValue)
}
return [input, handleChange]
}
表达式 setValue(eventValue)
导致以下错误:
Argument of type 'string
' is not assignable to parameter of type 'SetStateAction<T>
'.
我对此感到有点惊讶,因为很多导出的组件使用不同的 event.target.value
。例如日期选择器 return Date
类型,select Object
等
问题调查
当然我去检查导入的 ChangeEvent
react exports 看看它是否有正确的定义,但这似乎是正确的
interface ChangeEvent<T = Element> extends SyntheticEvent<T> {
target: EventTarget & T;
}
所以根据这个定义,它应该继承传递给 SyntheticEvent
的 Element
的类型
所以我沿着链条找到位于 node_modules/typescript/lib/lib.dom.d.ts
的 HTMLInputElement
声明,这就是问题的症结所在
interface HTMLInputElement extends HTMLElement {
value: string
//... rest
}
我回头检查了一下,似乎所有原生 <input>
元素都默认使用 string
作为它们的值类型,我想这是有道理的。
解决问题
显然这并不理想,因为这并不代表 event.target.value
许多使用第三方包的 reactjs 项目中的行为 (我的包应该支持)。考虑以下代码和框
returned event.target.value
与您对 typeof number
的期望一样
这让我想到了一个问题,我是否应该简单地用以下定义覆盖 ChangeEvent
?
ChangeEvent<{ value: T, name: string } & HTMLInputElement>
或者这会被视为不好的做法吗?或者是否有更好的方法来完全做到这一点?
handleChange
与要求的参数不匹配。
我已经尝试过并且有效:
export default function App() {
const [selected, setSelected] = useState(1);
const handleChange = (e: ChangeEvent<{
name?: string | undefined,
value: unknown | number
}>, child: React.ReactNode) => {
setSelected(e.target.value as number);
};
return (
<Select value={selected} onChange={handleChange}>
<MenuItem value={1}>One</MenuItem>
<MenuItem value={2}>Two</MenuItem>
<MenuItem value={3}>Three</MenuItem>
</Select>
);
}
好吧,我不是 100% 确定这是否是正确的方法,但它似乎适合我的 use-case,尽管输入看起来有点奇怪,但基本上我正在覆盖将类型参数传递给 ChangeEvent
并通过 HTML 元素之一的并集对其进行扩展。
export type FieldEvent<T> = ChangeEvent<
{ value: T, name?: string } &
(HTMLInputElement | HtmlTextAreaElement | HTMLSelectElement)
>
这会覆盖 ChangeEvent 的类型定义,然后您只需要创建一个扩展类型参数的处理函数
export type FieldHanderFunction<T> = (event: FieldEvent<T>) => void
所以在我的钩子里,它基本上归结为:
const useField<T> = (input: T) => {
const handleChange = (event: FieldEvent<T>) => {
// ...
}
}
我正在创作一个节点包,但我的 typescript 定义有点问题。更具体地说 我发现 event.target.value
的定义超级混乱
问题描述:
我有以下事件处理程序:
import { ChangeEvent, useState } from 'react'
type FieldEvent = ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
export const useField = <T>(input: T) => {
const [value, setValue] = useState<T>(input)
const handleChange = (event: FieldEvent) => {
const { name, value: eventValue } = event.target
// @ts-expect-error
setValue(eventValue)
}
return [input, handleChange]
}
表达式 setValue(eventValue)
导致以下错误:
Argument of type '
string
' is not assignable to parameter of type 'SetStateAction<T>
'.
我对此感到有点惊讶,因为很多导出的组件使用不同的 event.target.value
。例如日期选择器 return Date
类型,select Object
等
问题调查
当然我去检查导入的 ChangeEvent
react exports 看看它是否有正确的定义,但这似乎是正确的
interface ChangeEvent<T = Element> extends SyntheticEvent<T> {
target: EventTarget & T;
}
所以根据这个定义,它应该继承传递给 SyntheticEvent
Element
的类型
所以我沿着链条找到位于 node_modules/typescript/lib/lib.dom.d.ts
的 HTMLInputElement
声明,这就是问题的症结所在
interface HTMLInputElement extends HTMLElement {
value: string
//... rest
}
我回头检查了一下,似乎所有原生 <input>
元素都默认使用 string
作为它们的值类型,我想这是有道理的。
解决问题
显然这并不理想,因为这并不代表 event.target.value
许多使用第三方包的 reactjs 项目中的行为 (我的包应该支持)。考虑以下代码和框
returned event.target.value
与您对 typeof number
这让我想到了一个问题,我是否应该简单地用以下定义覆盖 ChangeEvent
?
ChangeEvent<{ value: T, name: string } & HTMLInputElement>
或者这会被视为不好的做法吗?或者是否有更好的方法来完全做到这一点?
handleChange
与要求的参数不匹配。
我已经尝试过并且有效:
export default function App() {
const [selected, setSelected] = useState(1);
const handleChange = (e: ChangeEvent<{
name?: string | undefined,
value: unknown | number
}>, child: React.ReactNode) => {
setSelected(e.target.value as number);
};
return (
<Select value={selected} onChange={handleChange}>
<MenuItem value={1}>One</MenuItem>
<MenuItem value={2}>Two</MenuItem>
<MenuItem value={3}>Three</MenuItem>
</Select>
);
}
好吧,我不是 100% 确定这是否是正确的方法,但它似乎适合我的 use-case,尽管输入看起来有点奇怪,但基本上我正在覆盖将类型参数传递给 ChangeEvent
并通过 HTML 元素之一的并集对其进行扩展。
export type FieldEvent<T> = ChangeEvent<
{ value: T, name?: string } &
(HTMLInputElement | HtmlTextAreaElement | HTMLSelectElement)
>
这会覆盖 ChangeEvent 的类型定义,然后您只需要创建一个扩展类型参数的处理函数
export type FieldHanderFunction<T> = (event: FieldEvent<T>) => void
所以在我的钩子里,它基本上归结为:
const useField<T> = (input: T) => {
const handleChange = (event: FieldEvent<T>) => {
// ...
}
}