如何在该场景下实现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
相关产品推荐
相关产品推荐

