如何让TypeScript识别父类抛异常后变量不可能为undefined?
解决方案
核心结论
不存在对应的tsconfig配置项可以实现该效果。TypeScript的控制流分析默认不会跨函数边界推导方法调用的副作用,这是出于编译性能的设计限制:如果要追踪所有父类方法内部的逻辑对实例属性的影响,大型项目的编译速度会出现不可接受的下降。
最优适配方案:使用断言函数
TypeScript 3.7以上版本支持asserts修饰符定义断言函数,可以在不修改原有业务逻辑、不使用非空断言的前提下,让TS识别属性的非空状态,完全适配你的存量代码场景,只需要给父类方法加一行类型标注即可:
针对示例代码的改造:
// 先定义父类实例ready后的非空类型 type AWithValidState = A & { someVar: number } class A { protected someVar: number | undefined; constructor(someVar: number | undefined) { this.someVar = someVar; } // 仅修改返回类型为断言类型,原有方法逻辑完全不变 protected ready(): asserts this is AWithValidState { if (this.someVar === undefined) throw Error('someVar undefined'); console.log('I AM READY!!!'); } public get someVarOfA(): number | undefined { return this.someVar; } }
改造后子类B调用完super.ready()后,TS会自动将this的类型收窄为AWithValidState,this.someVar会被识别为非空的number类型,不会再抛出类型错误。
针对实际SuperScene场景的改造:
只需要先定义好场景初始化后的非空状态类型,再修改父类ready方法的返回类型即可:
// 定义初始化完成后的场景类型,所有需要保证非空的属性都标注为必选 type ReadySuperScene = SuperScene & { sceneB: { sceneAssets: { images: NonNullable<SuperScene['sceneB']['sceneAssets']['images']> videos: NonNullable<SuperScene['sceneB']['sceneAssets']['videos']> documents: NonNullable<SuperScene['sceneB']['sceneAssets']['documents']> } // 其他需要非空的属性都可以在这里补充 } // 其他sceneC、sceneD等属性同理补充 } class SuperScene { // 仅修改ready方法的返回类型,原有逻辑完全不变 public ready(): asserts this is ReadySuperScene { // 原有校验抛出逻辑保留 } }
改造后所有子类调用完super.ready()后,不管是同步代码还是异步回调中的this.sceneB相关属性都会被TS自动识别为非空,不需要加任何额外判断,也不需要修改任何子类的业务逻辑,完全不会影响存量代码。
备用方案:父类方法签名不可修改时
如果因为某些限制不能修改SuperScene的方法签名,可以在子类中加一行极简的断言调用即可:
// 全局定义通用断言函数 function assertSceneReady(scene: SuperScene): asserts scene is ReadySuperScene { // 可以留空,因为你已经确定super.ready()会抛出异常保证状态合法,也可以加冗余校验兜底 } class SceneA extends SuperScene { public ready(): void { super.ready(); assertSceneReady(this); // 加这一行即可 // 后续所有代码都不会再报非空错误 } }
内容的提问来源于stack exchange,提问作者aProgger
相关产品推荐
相关产品推荐

