TypeScript报错:类型‘void | ISomeModel’不存在属性‘id’的原因及解决方法
你说得没错,实际运行时这个create函数要么返回ISomeModel要么抛出错误,但TypeScript是基于代码静态分析来推断类型的,它发现了一个潜在的void返回场景:
你的handleFirestoreError方法在处理permission-denied和unauthenticated错误时,只执行了tokenService.logout(true),没有抛出任何错误。这就意味着当触发这类错误时,create函数的catch分支会执行完handleFirestoreError后,默认返回undefined(对应类型void),而不是让Promise进入rejected状态。TypeScript捕捉到了这个逻辑,所以把create的返回类型推断为Promise<ISomeModel | void>。
有几种不同的修复方式,推荐从根源上对齐类型和实际逻辑的方案:
方案1:让handleFirestoreError始终抛出错误
既然你希望所有错误场景都会抛出异常,那修改handleFirestoreError,在处理完logout后明确抛出错误:
handleFirestoreError(error: any) { if (error.code === 'permission-denied' || error.code === 'unauthenticated') { tokenService.logout(true); // 新增:抛出错误,确保Promise进入rejected状态 throw error; } else { throw error; } }
这样修改后,create函数的catch分支永远会抛出错误,TypeScript就会推断它的返回类型为Promise<ISomeModel>,你的组件调用代码也不会再报错。
方案2:在create的catch分支补充抛出逻辑
如果不想修改handleFirestoreError的原有逻辑,也可以在create的catch块里,调用完错误处理方法后手动抛出错误:
create(someModel: ISomeModel): Promise<ISomeModel> { return db .add(someModel) .then((doc) => SomeService.fromJsonAndId(someModel, doc.id)) .catch((err) => { this.handleFirestoreError(err); // 确保Promise不会返回void throw err; }); }
方案3:类型断言(不推荐,仅临时绕过)
如果你确定运行时绝不会出现void的情况,可以用类型断言强制告诉TypeScript返回的是ISomeModel:
const someInstance = await someService.create(someModelObj) as ISomeModel; console.log('created', someInstance.id);
不过这种方式会跳过TypeScript的类型检查,隐藏潜在的运行时风险,只建议在紧急情况下临时使用。
内容的提问来源于stack exchange,提问作者rahulserver

