这是一个有关 TypeScript 类型推断的问题,代码如下
interface Form {
name: string,
age: number
height?: number
address?: number
}
const blankForm = {
name: '',
age: 24
}
const getBlankForm = (params?: Form): Form => {
const res: Form = { ...blankForm }
if (!params) return res
Object.keys(res).map(key => {
if (params[key as keyof Form] !== undefined)
res[key as keyof Form] = params[key as keyof Form]
})
return res
}
代码的倒数第 3
行 res[key as keyof Form] = params[key as keyof Form]
报错了,提示“Type 'string | number | undefined' is not assignable to type 'never'. Type 'undefined' is not assignable to type 'never'.ts(2322)”。具体地请看下图:
这里为什么对 res
键的访问为什么会被推断成 never
呢?
更新
有朋友就业务逻辑提出了解决方案,其实在业务上这个问题是完全可以被解决的,最简单的方法就是不使用 tsc
的检查,但问题到了这一步我单纯是对这个报错感到好奇和不解。谢谢各位的批评和意见。
谢邀,但是我确实不太清楚为什么是 never。
如果把
keyof Form
计算出来,这种情况下,如果有一个确定值的 key 是不会有问题的
但是如果 key 只知道类型是 FormKey,值不并确定的情况,比如函数中
就会得到不能赋值给 never 的错误提示。
为什么是 never 不太清楚,但是可以理解。你想,这时候 key 只知道是 FormKey 联合类型中一种,但具体是哪一种并不清楚。所以
res[key]
也就是 Form 属性值的类型是哪一个也不清楚。由于不同类型不能安全赋值,所以这里只能判定为不可赋值了。也许不能赋值的描述用 never 相对更贴合一些,所以用了这样的提示。做个实验,把 Form 所有属性都改为
string
,你会发现
res[key] = params[key]
仍然报错,但res[key] = params[key] ?? ""
是不会报错的。因为右边的值已经确定是 string 类型,而 Form 中无论哪一个属性,都可以把 string 类型的值赋给它。最后说一下这种问题怎么解决 ——
由于类型的不确定性,TypeScript 也不能通过静态类型的计算来获得准确的结果,那就只有报错。实际上,在运行时类型是可以确定的,所以这个问题在代码逻辑保证不会出现异常的情况下,可以转为
any
类型来处理,在运行时通过运行逻辑保证其正确性。其他语言的“反射”也是通过运行时类型计算/判断来保证正确性的,毕竟这事儿编译器真的干不了。类型计算不是万能的,不要把所有心思都花在类型计算上去。TypeScript 的类型运算已经非常复杂了,而且缺少调试工具,请适当的考虑动态类型检查。
最后再补一个试验,看起来很扯蛋 —— 其实他真的很扯蛋。但它能说明为什么会存在
m[key] !== m[key]
的可能性