Typescript对unknown类型做类型守卫时属性不存在报错如何解决?
问题原因
你遇到的报错来自两方面:
- 当你用
typeof obj === 'object' && obj将unknown类型收窄为object类型后,原生object类型没有定义任意属性的索引签名,哪怕你使用'foo' in obj判断属性存在,低于4.9版本的TypeScript也不会自动为object类型扩展该属性的类型定义,直接访问obj.foo就会触发不存在属性的报错。 - 你写的自定义类型守卫存在三个问题:
- 返回类型里写的
{ foo: 'string' }表示foo属性的值为字面量'string',而非foo属性类型为string,和你的判断逻辑不匹配 - 守卫函数内部判断时依然直接访问
obj.foo,此时还没有完成类型收窄,同样会触发报错 - 类型守卫函数要求返回布尔值标识入参是否符合目标类型,你直接返回了obj对象,不符合守卫函数的语法要求
- 返回类型里写的
解决方案
方案1:临时断言为可索引对象(兼容所有TS版本)
在完成object类型收窄后,先将对象断言为Record<string, unknown>,就可以安全访问任意属性做后续判断:
const foo = (obj: unknown) => { if (typeof obj === 'object' && obj) { const temp = obj as Record<string, unknown> if ('foo' in temp && typeof temp.foo === 'string') { return temp.foo; } } };
方案2:正确实现自定义类型守卫
如果你需要复用类型判断逻辑,可以按以下方式编写类型守卫:
// 类型守卫函数,判断入参是否是带有string类型foo属性的对象 const isObjWithFoo = (obj: unknown): obj is { foo: string } => { if (typeof obj !== 'object' || obj === null) return false return 'foo' in obj && typeof (obj as Record<string, unknown>).foo === 'string' }; // 使用示例 const getFoo = (obj: unknown) => { if (isObjWithFoo(obj)) { // 这里obj已经被自动收窄为{ foo: string }类型,访问foo不会报错 return obj.foo } return undefined }
方案3:升级TypeScript版本
如果你的项目可以升级依赖,将TypeScript升级到4.9及以上版本,新版本优化了in操作符的类型收窄逻辑,你最开始写的第一版代码不需要任何修改就可以正常编译运行。
内容的提问来源于stack exchange,提问作者Vinz243
相关产品推荐
相关产品推荐

