TypeScript中类型守卫在else if块中被推断为never的原因咨询
TypeScript中类型守卫在else if块中被推断为never的原因咨询
我来帮你拆解这个问题,先把你的代码贴出来方便咱们分析:
export default abstract class A { isB(): this is B { return false; } isC(): this is C { return false; } } export class B extends A { isB(): this is B { return true; } } export class C extends A { isC(): this is C { return true; } } export function printType(a: A) { if (a.isB()) { console.log("B"); } else if (a.isC()) { console.log("C"); // 这里TypeScript推断a的类型为never } else { console.log("A"); } }
出现这个现象的核心原因,要结合TypeScript对抽象类的实例范围限制,以及类型谓词的缩小逻辑来看:
抽象类的实例范围限制
你定义的A是抽象类,TypeScript门儿清:根本不存在直接的A类实例,所有标注为A类型的变量,实际都是它的子类B或C的实例。第一个条件后的类型缩小
当第一个if (a.isB())判断为false时,TypeScript会根据A类里isB()的类型谓词this is B,把a的类型缩小成Exclude<A, B>——说白了就是排除掉B类型,剩下的只有C类型了。else if分支的类型矛盾点
这时候进入else if分支,a的类型已经被锁死成C了。而C类重写的isC()方法,它的类型谓词是this is C,对于C类型的实例来说,这个方法必然返回true。关键就在这儿:TypeScript在类型检查阶段会认为,当
a不是B的时候,它100%是C,所以a.isC()返回true是板上钉钉的事儿——这个else if的条件完全是多余的。那为啥会推断出
never类型呢?其实这是TypeScript类型系统处理子类重写类型谓词时的一个边界小问题:当它发现某个分支的条件是“必然成立”,但类型缩小已经到了尽头,就会错误地把变量类型推断成never——简单说就是,它觉得“已经没有需要进一步缩小的类型空间了”,所以给出了这个看起来奇怪的结果。
要是你想让类型推断更符合预期,可以换个方式做类型判断,比如用instanceof,这样逻辑会更直观:
export function printType(a: A) { if (a instanceof B) { console.log("B"); } else if (a instanceof C) { console.log("C"); } else { console.log("A"); // 这里a的类型是never,因为A是抽象类确实没有实例 } }
内容来源于stack exchange
相关产品推荐
相关产品推荐

