You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
  );
};

额外注意事项

  1. 输入框的name属性必须和你要更新的状态字段严格对应,否则统一处理逻辑会失效;
  2. 如果字段原本是数字类型,输入框的value只能是字符串,需要在设置状态时做转换,提交时再转回数字,避免类型不匹配;
  3. 你原代码里输入框的name写的是mazeWidth(对应Player Name输入),这是明显的笔误,一定要修正。

内容的提问来源于stack exchange,提问作者Waiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 18:22:33