增加TypeScript类型系统限制为何能解决部分编译错误?
为什么开启
strictNullChecks反而会消除第三方库引发的TS构建报错 问题背景
- 构建TypeScript项目时出现编译报错,经排查问题根源来自引入的第三方库类型定义错误,最终尝试后发现解决方案是开启
strictNullChecks编译选项。 - 这个现象完全违背常规直觉:正常来说给类型系统增加更多限制,应该会触发更多类型报错才对,为什么加限制反而消除了已有错误?
- 该问题在discord.js的用户反馈中也被多次提及,有用户直接指出:
一个按照
strict模式标准编写类型的项目,反而在非严格配置下无法完成构建,完全不符合常理。
- 尝试直接阅读该库的类型定义源码排查根因时,发现代码中使用了大量嵌套深层泛型的TypeScript高级特性,且没有配套的类型逻辑说明文档,很难自行梳理清楚底层运行逻辑,因此需要了解该现象背后的TypeScript类型检查原理。
核心原因
出现这种反直觉情况的核心逻辑非常简单:TypeScript的类型推导规则本身会随编译选项变化,不是一套固定规则通吃所有配置。很多人以为严格模式只是在原有检查逻辑上额外加了几层校验,实际上strictNullChecks的开关会直接改变整个类型系统的基础判定规则,很多条件类型、泛型工具的推导结果在开关前后会完全不一样,完全可能出现「开了严格检查反而报错更少」的情况。
落到第三方库类型报错的具体场景,基本都是下面几个问题叠加导致的:
- 条件类型分支匹配完全错位
现在几乎所有在严格模式下写的类型定义,都会单独做空值的分支处理,逻辑类似T extends null | undefined ? 空值处理逻辑 : 正常类型逻辑。当strictNullChecks关闭时,null和undefined是所有类型的子类型,反过来所有类型也都满足extends null | undefined的判定,这就导致所有传入的类型都会被错误匹配到作者专门给空值写的处理分支里,后续的类型推导完全偏离作者写代码时的预期,自然会冒出来各种逻辑上说不通的类型不匹配、属性不存在报错。开了strictNullChecks之后,只有真正的空值类型会走到空值处理分支,其他类型都走正常推导逻辑,结果和作者测试时的预期一致,报错自然就没了。 - 泛型约束校验直接失效
很多复杂类型会先通过内置泛型工具把类型里的空值、可选属性标记剔除,再做后续的类型约束校验。关了strictNullChecks的时候,空值会被判定为合法属于所有类型,用来剔除空值的工具类型会完全不起作用,导致传入的类型因为带着多余的空值属性,满足不了后续的泛型约束要求,直接触发报错。开了选项之后空值可以被正确识别和剔除,泛型实例化符合约束要求,就不会抛错。 - 类型推导出现歧义
非严格空检查模式下,空值和任何类型都是双向兼容的——空值可以赋值给普通类型,普通类型也可以赋值给空值,这会导致函数重载匹配、泛型推导的时候同时出现多个符合条件的匹配结果,TypeScript遇到确定不了唯一匹配结果的场景就会直接抛类型不匹配的错误。严格模式下类型兼容是明确的单向关系,推导结果唯一,就不会触发这类歧义报错。
说白了这类问题本质就是第三方库的类型定义只在严格模式下做了测试,压根没考虑非严格模式的兼容,才会出现这种看起来完全不合逻辑的构建表现。
内容的提问来源于stack exchange,提问作者Cl00e9ment
相关产品推荐
相关产品推荐

