如何利用TypeScript泛型实现React输入组件onChange类型收窄?
可以实现,以下是修正方案
原代码的核心问题是:泛型参数TypeT在函数内部做类型判断时,TypeScript没法把宽泛的泛型约束收窄到具体的"number"或其他类型,导致调用onChange时出现类型不兼容的报错。
方案一:用联合类型定义Props(推荐)
直接把InputProps定义成包含不同type对应不同onChange参数的联合类型,让TypeScript自动完成类型收窄:
import React, { ChangeEvent } from 'react' type InputProps = | { type: 'number'; onChange: (value: number) => void } | { type: Exclude<React.HTMLInputTypeAttribute, 'number'>; onChange: (value: string) => void } function Input({ type, onChange }: InputProps) { const handleChange = (event: ChangeEvent<HTMLInputElement>) => { if (type === 'number') { onChange(event.currentTarget.valueAsNumber) } else { onChange(event.currentTarget.value) } } return <input type={type} onChange={handleChange} /> } // 使用示例 <> Valid text: <Input type="text" onChange={(value) => console.log(value)} /> {/* value自动推断为string */} Valid number: <Input type="number" onChange={(value) => console.log(value)} /> {/* value自动推断为number */} Invalid: <Input type="text" onChange={(value: number) => console.log(value)} /> {/* 直接触发TS报错,符合预期 */} </>
方案二:泛型结合类型断言(适合需保留泛型的场景)
如果必须保留泛型写法,可以在调用onChange时添加类型断言,同时通过泛型约束保证类型对应:
import React, { ChangeEvent, HTMLInputTypeAttribute } from 'react' export type InputProps<TypeT extends HTMLInputTypeAttribute> = { type: TypeT, onChange: (value: TypeT extends 'number' ? number : string) => void } function Input<TypeT extends HTMLInputTypeAttribute>({ type, onChange }: InputProps<TypeT>) { const handleChange = (event: ChangeEvent<HTMLInputElement>) => { if (type === 'number') { onChange(event.currentTarget.valueAsNumber as TypeT extends 'number' ? number : string) } else { onChange(event.currentTarget.value as TypeT extends 'number' ? number : string) } } return <input type={type} onChange={handleChange} /> } // 使用示例同前,类型推断依然生效
补充说明
- 方案一的联合类型写法更直观,不需要额外的类型断言,TypeScript能完美完成类型收窄,是更推荐的实现方式。
- 两种方案都能实现需求:当
type为"number"时,onChange的参数自动匹配number类型;其他type时参数为string类型,错误的类型传入会直接触发TypeScript报错。
内容的提问来源于stack exchange,提问作者Jakub Kotrs
相关产品推荐
相关产品推荐

