TypeScript编译器比较时异常类型推断问题咨询
问题:TypeScript中Lexer类skipComment方法的TS2367错误解析
我定义了一个Lexer类,其中的skipComment方法在第一种写法下触发TS2367错误:“This comparison appears to be unintentional because the types '#' and '\n' have no overlap.”。我猜测TypeScript编译器认为,在通过this.currChar === '#'的判断后,while循环的条件this.currChar !== '\n'会永远为真,但实际上nextChar方法会修改currChar的值,条件可能变为false。奇怪的是,如果在if判断后手动给this.currChar赋值为'0',方法就能正常运行,我搞不懂这个逻辑,求解答。
第一种写法(报错)
class Lexer { currChar: string; currPos: number; code: string; nextChar(): void { this.currPos += 1; if (this.currPos >= this.code.length) { this.currChar = '\\0'; } else { this.currChar = this.code.charAt(this.currPos); } } skipComment(): void { if (this.currChar === "#") { while (this.currChar !== "\\n") { this.nextChar(); } } } }
第二种写法(正常运行)
skipComment(): void { if (this.currChar === "#") { this.currChar = '0'; while (this.currChar !== "\\n") { this.nextChar(); } } }
原因分析
TypeScript的类型窄化机制在这里出了问题:
- 当你用
if (this.currChar === "#")判断后,编译器会把this.currChar的类型精准窄化为字面量类型'#',它默认认为在这个代码块里,currChar的值不会改变。 - 而
'#'和'\n'是完全不重叠的类型,所以编译器觉得this.currChar !== '\n'这个条件永远成立,属于毫无意义的判断,直接抛出TS2367错误。 - 但实际上
nextChar()会修改this.currChar的值,可惜TypeScript的静态分析没法追踪到方法内部对类属性的修改,所以它不知道currChar会在循环里被换掉。
第二种写法为什么能运行?
在if判断后手动赋值this.currChar = '0',相当于直接打破了TypeScript的类型窄化:
- 赋值后,
this.currChar的类型从严格的'#'变回了宽泛的string类型,此时this.currChar !== '\n'就成了有效的字符串比较,编译器自然不再报错。
正确的解决思路
没必要手动赋值无关字符,有两种更合理的方式:
方式1:用类型断言告诉编译器类型
skipComment(): void { if (this.currChar === "#") { while ((this.currChar as string) !== "\n") { this.nextChar(); } } }
方式2:改用do-while循环(更符合逻辑)
因为当前字符已经是#,我们本来就要跳过它之后的内容,所以先执行一次nextChar()再判断,这样一开始就避开了#的类型窄化问题:
skipComment(): void { if (this.currChar === "#") { do { this.nextChar(); } while (this.currChar !== "\n"); } }
内容的提问来源于stack exchange,提问作者Da Vinci
相关产品推荐
相关产品推荐

