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

TypeScript泛型使用报错:'not assignable'问题求助

解决useInput Hook中SetStateAction类型不匹配问题

你遇到的错误根源是:TypeScript编译阶段无法通过typeof init的运行时判断,自动缩小泛型T的具体类型,导致setInput接收的参数类型与期望的SetStateAction<T>不兼容。以下是两种简洁的修复方案,无需拆分两个独立函数:

方案一:类型断言快速修复

直接在每个分支中通过类型断言告诉TypeScript当前赋值类型符合泛型T的约束,同时优化数字类型的转换逻辑:

import { useState } from "react";

type useInputT<T> = {
  value: T;
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};

export default function useInput<T extends string | number>(init: T): useInputT<T> {
  const [input, setInput] = useState<T>(init);

  function onChange(e: React.ChangeEvent<HTMLInputElement>): void {
    switch (typeof init) {
      case "string":
        setInput(e.target.value as T);
        break;
      case "number":
        // 处理空输入或非数字内容,避免NaN进入状态
        const num = parseInt(e.target.value, 10);
        setInput(isNaN(num) ? 0 : num as T);
        break;
    }
  }

  return { value: input, onChange };
}

方案二:类型守卫增强类型安全性

如果需要更严谨的类型推断,可以自定义类型守卫函数,让TypeScript精确识别分支中的T类型:

import { useState } from "react";

type useInputT<T> = {
  value: T;
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};

// 自定义类型守卫,缩小T的类型范围
function isString<T>(val: T): val is T & string {
  return typeof val === "string";
}

function isNumber<T>(val: T): val is T & number {
  return typeof val === "number";
}

export default function useInput<T extends string | number>(init: T): useInputT<T> {
  const [input, setInput] = useState<T>(init);

  function onChange(e: React.ChangeEvent<HTMLInputElement>): void {
    if (isString(init)) {
      setInput(e.target.value as T);
    } else if (isNumber(init)) {
      const num = parseInt(e.target.value, 10);
      setInput(isNaN(num) ? 0 : num as T);
    }
  }

  return { value: input, onChange };
}

额外优化建议

如果是数字类型的输入,建议将<input>的type设为"number",使用valueAsNumber直接获取数字值,避免字符串转数字的潜在问题:

const inputNumber = useInput<number>(1);

return (
  <input
    type="number"
    value={inputNumber.value}
    onChange={inputNumber.onChange}
  />
);

对应的onChange分支修改为:

case "number":
  const num = e.target.valueAsNumber;
  setInput(isNaN(num) ? 0 : num as T);
  break;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 06:22:03