Kodokon kodokon.com

严格的 tsconfig:关键选项与常见错误

你将理解严格模式实际启用了什么,并知道如何处理它在既有项目中揭示出的典型错误。

11 分钟 · 3 题

在 Kodokon 中打开本课

"strict": true 并不是单个选项,而是一个启用整整一族检查的聚合项strictNullChecksnoImplicitAnystrictFunctionTypesstrictBindCallApplystrictPropertyInitializationnoImplicitThisuseUnknownInCatchVariables 以及 alwaysStrict。每个新的 TypeScript 版本都可能再增加更多:启用 strict 就等于订阅了未来的检查。每个标志仍可被单独关闭,这使得在既有代码库上进行渐进式迁移成为可能。

JSON
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    "verbatimModuleSyntax": true,
    "skipLibCheck": true
  }
}
一个用聚合项之外的选项加以扩展的严格基线(JSON 文件)。

影响力最大的两个标志是 strictNullChecksnoImplicitAny。前者从所有类型中移除 nullundefined:任何可能缺失的值都必须被声明为 T | undefined 并在使用前处理。后者则拒绝让未标注的参数退回到 any。二者结合,消除了 JavaScript 两大类 bug:“cannot read property of undefined”以及未经检查的值的扩散。useUnknownInCatchVariables 补全了这幅图景:catch 中的变量是 unknown,因为 JavaScript 允许你抛出任何东西。

TYPESCRIPT
function findPort(
  env: Record<string, string | undefined>
): number {
  const raw = env["PORT"];
  if (raw === undefined) {
    return 3000;
  }
  return Number.parseInt(raw, 10);
}

declare function riskyOperation(): void;

try {
  riskyOperation();
} catch (err) {
  const message =
    err instanceof Error ? err.message : String(err);
  console.error(message);
}
两个严格模式模式:处理了缺失情况,catch 作为 unknown。

常见的迁移错误及其真正的解法。“Object is possibly undefined”:优先使用收窄(if)、可选链 ?.?? 默认值,而不是非空断言 !,后者只是把崩溃转移到别处。“Property has no initializer”(strictPropertyInitialization):在构造函数中初始化,而不是在字段上动用 !。还要当心 ??|| 的陷阱:port || 3000 会连 0"" 也一并替换掉,而 port ?? 3000 只替换 nullundefined

TYPESCRIPT
interface Settings { retries?: number }

function resolveRetries(s: Settings): number {
  const bad = s.retries || 3;
  const good = s.retries ?? 3;
  return good;
}

const names = ["Ada", "Linus"];
const first = names[0];
const upper = first?.toUpperCase() ?? "N/A";
retries: 0 会让 bad = 3 但 good = 0;接下来会对索引访问进行检查。

在聚合项之外,有三个选项值得团队讨论。noUncheckedIndexedAccess 会给每一次索引访问(arr[i]dict[key])都加上 undefined:更安全,但在数组操作代码中会显得冗长——许多团队只在新项目上采用它。exactOptionalPropertyTypes 区分“属性缺失”与“属性被设为 undefined”,对补丁至关重要。skipLibCheck 会跳过对依赖项 .d.ts 文件的检查:这是在构建时间与检测库之间冲突之间的一个刻意取舍。

知识检测

确认你已牢记本课的重点内容。

  1. "strict": true 在 tsconfig.json 中究竟做了什么?
    • 它只启用 strictNullChecks 选项
    • 它启用一组严格标志,其中每个都仍可被单独关闭
    • 它禁止任何对 any 类型的使用,即使是显式的
    • 它强制以 ES5 模式编译
  2. retries?: number 被设为 0 时,retries || 3retries ?? 3 有什么区别?
    • 没有区别,两者都返回 0
    • || 返回 3,因为 0 是假值;?? 返回 0,因为它只替换 null 和 undefined
    • ?? 返回 3,因为 0 被当作缺失处理
  3. 为什么在标记类型债时优先选择 // @ts-expect-error 而非 // @ts-ignore
    • @ts-expect-error 能一次抑制多个错误
    • @ts-ignore 在严格模式下不起作用
    • @ts-expect-error 会在问题被修复后自身变成错误,于是技术债会自行清理