strict 模式全解:把隐患挡在编译期
本节目标
- 搞懂
strict打开后具体有哪些子选项生效 - 理解
strictNullChecks为什么是最重要的一项 - 学会「逐步开启 strict」的平滑迁移策略
"strict": true 其实是下面这些子选项的「总开关」:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true, // 不允许隐式 any
"strictNullChecks": true, // 禁止把 null/undefined 赋给非可空类型
"strictFunctionTypes": true, // 函数参数逆变检查更严
"strictBindCallApply": true, // bind/call/apply 也做类型检查
"strictPropertyInitialization": true, // 类属性必须初始化
"noImplicitThis": true, // this 类型不明确时报错
"alwaysStrict": true // 输出 "use strict"
}
}其中最关键的是 strictNullChecks。关掉时,null 能赋给任何类型,于是 user.name 在 user 为 null 时也不报错——这是 JS 里 Cannot read 'x' of null 的根源。
// 开启 strictNullChecks 后
function getName(u: { name: string } | null): string {
// return u.name // ❌ u 可能是 null,必须先判空
return u ? u.name : '匿名'
}
// 渐进式迁移老项目:先不开总开关,单独开最关键的
// "strictNullChecks": true // 单独开这一项,收益最大、破坏最小名词解释
strictNullChecks:strict 家族里最重要的一项。开启后,null 和 undefined 不再是「能塞进任何类型的万能值」,而是必须被显式标注(T | null)并判空。它直接消灭了 JS 头号运行时错误「无法读取 null 的属性」。代价是老代码要补大量判空——但这是值得的。
渐进式严格化(Gradual Strictness):老项目不敢一次性开 strict 时,先单独开 strictNullChecks,再逐步开 noImplicitAny 等。像「先系安全带再提速」,既拿到最大收益(杜绝空指针),又不至于改动爆炸。
课后练习
练习 1:为什么 strictNullChecks 比 noImplicitAny 对运行时安全影响更大?
答案:
noImplicitAny主要防止「类型信息丢失导致提示变弱」;而strictNullChecks直接防止「把 null 当对象用」这种会崩的运行时代码被编译通过。前者影响开发体验,后者影响线上稳定性,所以后者更关键。
练习 2:类里 strictPropertyInitialization 报错「属性未初始化」怎么解?
答案:三种方式:① 声明时给默认值
name = '';② 构造函数里赋值;③ 确定运行时会赋值则用name!: string(非空断言属性,谨慎用)。
总结
我的立场从不摇摆:新项目直接 "strict": true 起步,没有商量余地。它短期让你多写几行判空和注解,长期替你挡掉一整类最致命的线上事故。对老项目,我的迁移策略是「先开 strictNullChecks 单项」——这一项性价比最高、破坏最小,团队最容易接受;跑顺了再逐个加 noImplicitAny。千万别反过来「图省事先关 strict,以后再说」,因为「以后」永远不会来,欠的类型债越积越难还。记住:类型严格不是给编译器看的表演,是给你自己买的保险。