React+TypeScript技术问题:如何解决‘仅能从对象类型创建扩展类型’报错?
解决React表单统一处理中的TypeScript扩展运算符类型错误
你遇到的Spread types may only be created from object types.ts(2698)错误,核心原因非常清晰:prevState是string类型(因为name是用useState声明的字符串状态),而扩展运算符...只能用于对象、数组这类可迭代的对象类型,直接对字符串使用当然会触发类型错误。
再看你的代码逻辑:你把表单的每个字段拆成了独立的useState(name和height),但handleInputChange里却错误地把字符串类型的prevState当成对象来扩展,同时还存在变量重名(把输入框的值赋值给了和状态变量同名的name)、输入框name属性和状态不匹配的笔误,这些都是问题的诱因。
下面给你两种可行的解决方案:
方案1:合并状态为单个对象(推荐,适配多字段表单)
把分散的字段状态合并成一个对象,这样统一处理onChange会更清晰,也符合你想要统一管理的需求:
interface Props { mazeSettup: MazeSettup; } // 定义表单状态的类型,和字段一一对应 interface FormState { playerName: string; mazeHeight: string; // 如果原mazeHeight是数字类型,这里用字符串适配输入框value } export const GameSettupForm: React.FC<Props> = ({ mazeSettup }) => { // 初始化合并后的对象状态 const [formState, setFormState] = useState<FormState>({ playerName: mazeSettup.playerName, mazeHeight: mazeSettup.mazHeight.toString() }); const handleInputChange = React.useCallback( (event: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = event.target; // 此时prevState是对象类型,扩展运算符完全合法 setFormState(prevState => ({ ...prevState, [name]: value // 用输入框的name属性动态匹配要更新的字段 })); },[]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // 提交时可按需转换类型,比如把mazeHeight转回数字 const submitData = { ...mazeSettup, playerName: formState.playerName, mazHeight: parseInt(formState.mazeHeight, 10) }; // 这里写你的提交逻辑 }; return ( <Form onSubmit={handleSubmit}> <label htmlFor="playername"> Player Name <input type="text" className="form-control" id="playername" name="playerName" // name属性要和FormState的键严格对应 placeholder="Player Name" value={formState.playerName} onChange={handleInputChange} /> </label> <label htmlFor="mazeheight"> Maze Height <input type="text" name="mazeHeight" // 对应FormState的键 placeholder="Maze Height" value={formState.mazeHeight} onChange={handleInputChange} /> </label> </Form> ); };
方案2:保留独立状态,调整统一处理函数
如果你不想合并状态,也可以通过判断输入框的name属性,调用对应的状态更新函数:
interface Props { mazeSettup: MazeSettup; } export const GameSettupForm: React.FC<Props> = ({ mazeSettup }) => { const [name , setName] = useState(mazeSettup.playerName); const [height, setHeight] = useState(mazeSettup.mazHeight.toString()); const handleInputChange = React.useCallback( (event: React.ChangeEvent<HTMLInputElement>) => { const { name: fieldName, value } = event.target; // 根据输入框的name字段,匹配对应的状态更新函数 switch(fieldName) { case "playerName": setName(value); break; case "mazeHeight": setHeight(value); break; default: break; } },[]); // 提交逻辑... return ( <Form onSubmit={handleSubmit}> <label htmlFor="playername"> Player Name <input type="text" className="form-control" id="playername" name="playerName" placeholder="Player Name" value={name} onChange={handleInputChange} /> </label> <label htmlFor="mazeheight"> Maze Height <input type="text" name="mazeHeight" placeholder="Maze Height" value={height} onChange={handleInputChange} /> </label> </Form> ); };
额外注意事项
- 输入框的
name属性必须和你要更新的状态字段严格对应,否则统一处理逻辑会失效; - 如果字段原本是数字类型,输入框的
value只能是字符串,需要在设置状态时做转换,提交时再转回数字,避免类型不匹配; - 你原代码里输入框的
name写的是mazeWidth(对应Player Name输入),这是明显的笔误,一定要修正。
内容的提问来源于stack exchange,提问作者Waiz
相关产品推荐
相关产品推荐

