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

如何在该场景下实现TypeScript类型收窄?

如何在该场景下实现TypeScript类型收窄?

刚好之前做项目时遇到过几乎一模一样的问题,来给你捋捋根因和解决办法~

你碰到的这个Type 'string' is not assignable to type 'never'错误,本质是TypeScript的类型推断逻辑在搞事情:当draft的属性有多种不同类型(比如既有字符串又有布尔值)时,draft[name]的类型会被推断为所有属性类型的交集——不同类型的交集自然就是never,所以你把字符串赋值进去肯定会报错。

下面给你几个实用的解决方案,按需选就行:

方案1:针对不同Input类型处理对应值(简单直接)

React里不同Input的取值逻辑不一样,比如复选框要拿target.checked,文本框拿target.value,我们可以先通过target.type做类型守卫,拿到对应类型的值再赋值:

const updateEditedRecord = (event: React.ChangeEvent<HTMLInputElement>) => {
  const { target } = event;
  // 根据Input类型获取对应的值
  let val: string | boolean;
  if (target.type === "checkbox") {
    val = target.checked;
  } else {
    val = target.value;
  }

  setEditedRecord((draft) => {
    const name = target.name as keyof typeof draft;
    // 用类型断言告诉TS当前值的类型和属性匹配
    draft[name] = val as typeof draft[keyof typeof draft];
    return draft;
  });
};

这个方案适合大部分常见的表单场景,唯一要注意的是如果你的数据里有数字这类其他类型,得再加对应的判断分支。

方案2:用泛型做精确类型映射(类型安全拉满)

如果你的editedRecord有明确的结构,先定义一个接口,再用泛型让TypeScript精确对应每个属性的类型:

首先定义你的数据接口:

interface UserRecord {
  username: string;
  isActive: boolean;
  age: number;
}

然后修改更新函数:

const updateEditedRecord = <K extends keyof UserRecord>(
  event: React.ChangeEvent<HTMLInputElement & { name: K }>
) => {
  const { target } = event;
  let val: UserRecord[K];

  // 根据属性名匹配对应的类型转换逻辑
  switch (target.name) {
    case "age":
      val = Number(target.value) as UserRecord[K];
      break;
    case "isActive":
      val = target.checked as UserRecord[K];
      break;
    default:
      val = target.value as UserRecord[K];
      break;
  }

  setEditedRecord((draft) => {
    draft[target.name] = val;
    return draft;
  });
};

这个方案能让TypeScript在编译阶段就帮你检查所有属性的类型,完全不会有类型错误,适合对类型安全性要求高的场景。

方案3:自定义类型守卫做运行时检查(灵活度高)

如果不想提前定义接口,也可以写自定义类型守卫,在运行时判断属性类型再赋值:

// 自定义类型守卫:判断属性是否为字符串类型
function isStringProperty<T>(obj: T, key: keyof T): key is keyof { [K in keyof T]: T[K] extends string ? K : never } {
  return typeof obj[key] === "string";
}

// 自定义类型守卫:判断属性是否为布尔类型
function isBooleanProperty<T>(obj: T, key: keyof T): key is keyof { [K in keyof T]: T[K] extends boolean ? K : never } {
  return typeof obj[key] === "boolean";
}

const updateEditedRecord = (event: React.ChangeEvent<HTMLInputElement>) => {
  const { target } = event;

  setEditedRecord((draft) => {
    const name = target.name as keyof typeof draft;
    if (isStringProperty(draft, name)) {
      draft[name] = target.value;
    } else if (isBooleanProperty(draft, name)) {
      draft[name] = target.checked;
    }
    // 如果有数字类型,再加对应的类型守卫和赋值逻辑
    return draft;
  });
};

这种方式不需要提前定义接口,通过运行时检查来让TypeScript自动收窄类型,灵活性很强。

备注:内容来源于stack exchange,提问作者pixelearth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:13:04