严格模式就是 tsconfig.json 中的 "strict": true 选项。它是八个类型检查选项的总开关,能捕获隐式的 any、未检查的 null 和 undefined、不安全的函数赋值以及未初始化的类字段。在 TypeScript 7 中,strict 默认开启。能通过严格检查的代码是这样的:
输出是 no user、SyntaxError 和 42。去掉 : number、?. 或 instanceof 检查中的任何一个,文件就无法编译了。
开启严格模式
{
"compilerOptions": {
"strict": true
}
}
单独的选项总是优先于 strict,两个方向都一样。"strict": true, "strictNullChecks": false 保留除空值检查以外的一切;"strict": false, "noImplicitAny": true 只开启这一项。strict 还会让你自动获得未来版本新增的检查,因为新的 strict 系列选项都会加入这一组。
TypeScript 的 strict 与 JavaScript 的 "use strict" 指令毫无关系,后者是一种运行时模式。TypeScript 7 会在需要的地方自动生成 "use strict",而设置 alwaysStrict: false 会报错 TS5108(该选项已被移除)。
每个选项能捕获什么
| 选项 | 报告的问题 | 错误 |
|---|---|---|
noImplicitAny | 类型会被悄悄当作 any 的参数或变量 | TS7006 Parameter 'x' implicitly has an 'any' type. |
strictNullChecks | 使用可能为 null 或 undefined 的值 | TS18048 'u' is possibly 'undefined'. |
strictFunctionTypes | 赋值一个参数类型比要求更窄的函数 | TS2322 |
strictBindCallApply | 给 .call、.bind 和 .apply 传错参数 | TS2345 |
strictPropertyInitialization | 从未被赋值的类字段 | TS2564 Property 'name' has no initializer and is not definitely assigned in the constructor. |
noImplicitThis | 隐式为 any 类型的 this,比如在嵌套的 function 中 | TS2683 |
useUnknownInCatchVariables | 未经收窄就使用 catch 变量(它的类型是 unknown) | TS18046 'err' is of type 'unknown'. |
strictBuiltinIteratorReturn | 把内置迭代器的 it.next().value 当作总是有值 | TS2322 |
改动代码最多的两项是 noImplicitAny 和 strictNullChecks。下面这段代码故意同时违反了这两项:
它会输出 index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type. 和 index.ts(11,13): error TS18048: 'user' is possibly 'undefined'.。如果不开启严格模式,这段代码能编译通过,然后在运行时崩溃并报 TypeError: Cannot read properties of undefined (reading 'name')。开启严格模式后,运行时崩溃就变成了编译错误。修复方法就是本页的第一段代码。
strictFunctionTypes:为什么需要它
只能处理字符串的函数,不能用在可能传入数字的地方。下面的检查被 @ts-expect-error 压制住了,这样你可以运行它,看看这个错误阻止了什么:
去掉这行注释,这个赋值就会报错 TS2322:Type '(s: string) => void' is not assignable to type 'Handler'.,后面跟着 Types of parameters 's' and 'value' are incompatible.。有一个例外是有意保留的:用方法语法声明的方法参数(接口中的 handle(value: string | number): void)仍按较宽松的方式检查,所以把 shout 赋给这样的方法不会报错。
strictPropertyInitialization
每个类字段都必须在声明处或构造函数中获得一个值。有三种方式满足这一要求:
输出:
Account {
owner: 'Ada',
balance: 0,
history: [],
lastLogin: undefined,
sessionId: 's-1'
}
lastLogin 和 sessionId 属性存在,值为 undefined,因为在 ES2022 目标下,类字段是真正的 JavaScript 字段。! 会去掉检查,但不提供任何运行时保护,所以优先使用其他写法。这个选项依赖 strictNullChecks,关掉后者它也会一起失效。
在现有项目中开启严格模式
一次性把大型代码库切换到 strict 可能会产生上百个错误。由于 strict 是 TypeScript 7 的默认值,升级一个 tsconfig.json 从未提到 strict 的项目时,它会自动开启;如果迁移期间需要旧的行为,就写上 "strict": false。下面这条路线可以让构建始终保持通过:
- 加上
"strict": true,并关闭错误最多的几个选项,通常是"strictNullChecks": false和"noImplicitAny": false。 - 修复剩下的错误,然后再开启一个选项,重复这个过程。
- 对于
noImplicitAny,大多数修复就是给参数加类型注解。对于strictNullChecks,在值可能缺失的地方加上| undefined,再用?.、??或if判断来处理。 - 暂时没法修的地方,在那一行写上
// @ts-expect-error并注明原因。与@ts-ignore不同,问题消失后它会报错,所以这份清单会自己慢慢变短。
不要用 as any 或 ! 来批量消除错误:每一个都恰好隐藏了该选项本来要找出的 bug。
strict 不包含的实用选项
这些选项是独立的,因为它们会拒绝很多其实正确的代码。不过很多项目还是会开启它们。
| 选项 | 作用 |
|---|---|
noUncheckedIndexedAccess | arr[i] 和 record[key] 的类型中包含 undefined |
exactOptionalPropertyTypes | debug?: boolean 允许缺少这个键,但不允许 debug: undefined |
noImplicitReturns | 有返回值的函数,每条代码路径都必须返回(TS7030) |
noImplicitOverride | 重写基类方法的方法必须写 override(TS4114) |
noFallthroughCasesInSwitch | 非空的 case 必须以 break、return 或 throw 结尾(TS7029) |
noUnusedLocals、noUnusedParameters | 未使用的变量和参数会报错 |
noPropertyAccessFromIndexSignature | 来自索引签名的键必须用 obj["key"] 访问,而不是 obj.key |
noUncheckedIndexedAccess 能捕获最多的真实 bug。开启后:
const scores = [90, 85];
const d: Record<string, number> = {};
const third: number = scores[2]; // error TS2322: Type 'number | undefined' is not assignable to type 'number'.
const c: number = d["x"]; // same error
const safe = scores[2] ?? 0; // number
for (const s of scores) { // for...of is not affected: s is number
console.log(s);
}
TypeScript 7 中的 tsc --init 会在生成的配置里,在 strict 旁边开启 noUncheckedIndexedAccess 和 exactOptionalPropertyTypes。
常见问题
TypeScript 的严格模式有什么作用?
"strict": true 会开启一组更严格的检查:noImplicitAny、strictNullChecks、strictFunctionTypes、strictBindCallApply、strictPropertyInitialization、noImplicitThis、useUnknownInCatchVariables 和 strictBuiltinIteratorReturn。它们合在一起,能阻止 any 悄悄出现,并让 null 和 undefined 成为类型系统的一部分。
TypeScript 默认开启严格模式吗?
在 TypeScript 7 中是的:strict 默认为 true,所以没有设置 strict 的项目也会获得所有严格检查。tsc --init 也会显式写入 "strict": true。想关闭它,必须写 "strict": false。
能只关闭某一项严格检查,保留其余的吗?
可以。单独的选项会覆盖 strict:{ "strict": true, "strictNullChecks": false } 保留除空值检查以外的所有严格检查。这是一次迁移一个选项、逐步改造大型代码库的常用做法。
TypeScript 严格模式和 JavaScript 的 "use strict" 是一回事吗?
不是。"use strict" 是 JavaScript 的运行时模式,会改变代码的行为。TypeScript 的 strict 只改变类型检查器报告的内容。TypeScript 7 对非模块输出总会生成 "use strict",而 alwaysStrict: false 现在是一个已移除的选项。
strict 包含 noUncheckedIndexedAccess 吗?
不包含。noUncheckedIndexedAccess、exactOptionalPropertyTypes、noImplicitReturns、noImplicitOverride 和 noFallthroughCasesInSwitch 都是需要你自己开启的独立选项。tsc --init 会在生成的配置中开启前两个。