深入理解 strictNullChecks:TypeScript Book 中的严格空值检查指南
2026/9/24 19:05:14 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

strictNullChecks是 TypeScript 编译器中最影响日常开发体验的选项之一:它决定了nullundefined是否会被当作普通值参与类型系统,还是被严格隔离、仅在显式声明时才允许出现。本文以《The Concise TypeScript Book》中 strictNullChecks 章节 为核心,结合仓库内类型系统、配置与配套语法章节,系统讲解该选项的语义、配置方式、与null/undefined类型的关系,以及在实际代码中配合使用的!?.??等工具,帮助你彻底掌控空值处理。

strictNullChecks 是什么

根据 strictnullchecks.md 的定义,strictNullChecks是 TypeScript 编译器的一个选项,它强制进行严格的空值检查(enforces strict null checking)。

当该选项被启用时,变量(variable)和参数(parameter)只有在显式声明其类型为null | undefined联合类型时,才允许被赋值为nullundefined。反过来,如果某个变量或参数没有被显式声明为可空(nullable),TypeScript 就会在编译期生成错误,从而提前阻止潜在的运行时错误(runtime errors)。

这一行为可以概括为两条规则:

  • 显式声明才能赋值:可空性必须通过联合类型null | undefined明确写出,编译器才允许传入空值;
  • 未声明即报错:任何未显式声明为可空的变量或参数接收到null/undefined时,编译直接失败。

例如在严格模式下,下面的代码会报错:

// 显式声明为可空:合法 const maybeName: string | null = null; // 未声明为可空:TypeScript 编译错误 // Type 'null' is not assignable to type 'string' const name: string = null;

这正是该选项的价值所在:将“空值可能随时出现”这一隐患从运行时提前到编译期,强制开发者显式处理空值路径。

在类型系统中的地位:null 与 undefined 的语义

要理解strictNullChecks,先要理解nullundefined在 TypeScript 类型系统中的语义。根据 primitive-types.md 的说明:

  • undefined表示值未被赋值或初始化,即“非有意的值缺失”(unintentional absence of value);
  • null表示我们明确知道该字段没有值,即“有意的值缺失”(intentional absence of value)。

在 exploring-the-type-system.md 的“类型作为集合”章节中,undefinednull被归类为单元素集合(Single element set),与never(空集合)、boolean/union(有限集合)、string/number/object(无限集合)、any/unknown(全集)并列。这意味着每个空值类型只包含一个可能的取值,它们的可赋值规则非常明确。

更关键的是,该书在同一章节明确给出了strictNullChecks对这两个类型行为的决定性影响:

请注意,当启用strictNullChecks时,nullundefined的处理方式与void类似;否则,它们与never类似。

这段话揭示了两种模式下的本质差异:

编译选项状态null/undefined的表现实际效果
strictNullChecks启用类似void,是类型系统中真实存在的值类型只有显式声明为null/undefined或与之联合的变量才能接收空值
strictNullChecks禁用类似never,可以被赋值给任意类型任何类型都“假装”接受null/undefined,空值检查形同虚设

也就是说,关闭该选项后,nullundefined几乎可以赋给任何类型(像never一样具有极强的可赋值性),此时类型系统无法约束空值的扩散;而开启后,它们被限制在显式声明的联合类型内,类型系统得以精准追踪每个可空位置。

如何启用:tsconfig.json 与 CLI

strictNullChecks的启用方式有两种:写入tsconfig.json,或通过tsc命令行参数传递。在日常项目中,推荐将其纳入strict严格模式统一管理。

通过 strict 严格模式启用

本项目tools目录下的 tsconfig.json 是实际启用了严格模式的示例:

{ "compilerOptions": { "target": "es2022", "module": "commonjs", "noImplicitAny": true, "esModuleInterop": true, "noEmitOnError": true, "moduleDetection": "force", "noUnusedLocals": false, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "lib": ["es2022", "esnext.disposable", "esnext.decorators", "dom"] } }

其中第 11 行的"strict": true会一次性启用包括strictNullChecks在内的一组严格检查选项。网站部分则在 website/tsconfig.json 中通过继承 Astro 的严格预设开启:

{ "extends": "astro/tsconfigs/strict" }

单独开启或关闭

如果不想启用整套严格模式,也可以只针对该选项单独配置:

{ "compilerOptions": { "strictNullChecks": true } }

将值改为false则单独关闭空值检查。同样的效果可以通过 CLI 达成:

tsc --strictNullChecks

需要说明的是:strictNullChecks的效果与项目实际使用的 TypeScript 版本密切相关,本文讨论的行为以仓库 tools/package.json 中锁定的typescript ^5.4.5为基准。

开启前后的行为对比

结合 exploring-the-type-system.md 中“记住这些规则”一节的赋值示例,可以看到可赋值性的完整图谱:

let a: number = 1; let b: number = 2; a = b; // 合法:一切都可以赋值给自己 let c: any; c = 1; // 合法:所有类型都可以赋给 any let d: unknown; d = 1; // 合法:所有类型都可以赋给 unknown let e: unknown; let e1: unknown = e; // 合法:unknown 只能赋给自身和 any let e2: any = e; // 合法 let e3: number = e; // 非法 let f: never; f = 1; // 非法:没有任何值可以赋给 never let g: void; let g1: any; g = 1; // 非法:void 除了 any 之外不接受也不可赋给任何类型 g = g1; // 合法

结合该章节的注释可以得出:在启用strictNullChecks的项目中,nullundefined的赋值行为应当类比void——它们只在显式声明的上下文中流转,不会“悄悄”渗入其他类型。这也是为什么开启后,类似document.getElementById(...)的 API 返回值会包含null分支,从而迫使开发者处理“元素可能不存在”的路径。

实际开发中的配套工具

启用strictNullChecks后,空值会频繁出现在类型中,TypeScript 也提供了配套语法让处理更简洁。《The Concise TypeScript Book》在 others.md 中系统介绍了这些工具。

Non-null Assertion 运算符(后置!

后置!用于向编译器断言某个变量或属性不是nullundefined,即便静态类型分析认为它可能是。该功能可以消除一部分显式的空值检查:

type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); };

如上例所示,person是可选参数(类型为Person | undefined),通过person!.name我们向编译器承诺此时person必然存在。需要强调的是,!只是编译期断言,不产生任何运行时保护——若实际值为空,运行时依然会抛错,因此应谨慎使用。

可选链?.

可选链运算符?.与普通点运算符.用法类似,区别在于它安全地处理null/undefined:当左侧值为空时,整个表达式短路并返回undefined,而不是抛出错误:

type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined

在严格空值检查下,person.address的类型是{ street?: string; city?: string } | undefined?.让我们无需逐层if判断即可安全访问深层属性。

空值合并运算符??

空值合并运算符??在左侧为nullundefined时返回右侧值,否则返回左侧值——注意它只对null/undefined生效,不会像||那样把0''false等 falsy 值一并替换:

const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0

??strictNullChecks配合是“为可空值提供默认值”的标准写法,且不会误伤合法的 falsy 值。

可选属性?与类型收窄

除了上述运算符,书中 optional-properties.md 章节讲解的可选属性语法prop?: T本质上是prop: T | undefined,与strictNullChecks直接相关。同时,narrowing.md 与 control-flow-analysis.md 介绍了通过typeof检查、truthiness 判断、in运算符、instanceof等方式在代码流中收窄可空类型的方法,例如:

let x: number | undefined = 10; if (x !== undefined) { x += 100; // 此处 x 已被收窄为 number }

这类控制流收窄是strictNullChecks模式下处理空值的主力手段,能让代码在保持安全的同时清晰表达“空值已被排除”的意图。

在本书中的位置与延伸阅读

strictNullChecks在《The Concise TypeScript Book》中位于 目录 的第 19 章,紧随 “Literal Inference” 之后,处于类型系统基础概念向进阶类型语法过渡的关键位置。围绕它,书中提供了完整的知识链路:

  • 前置基础:primitive-types.md(null/undefined语义)、exploring-the-type-system.md(类型系统与可赋值性);
  • 核心章节:strictnullchecks.md(本文主题);
  • 进阶衔接:narrowing.md(空值收窄)、optional-properties.md(可选属性)、unknown-type.md(类型安全的unknown)、the-never-type.md(never与穷尽性检查)。

此外,本书为多语言开源项目,本文依据的保加利亚语章节与英文原版 strictnullchecks.md 内容一致;除保加利亚语外,网站内容目录 下还维护了阿拉伯语、中文、日语、韩语、俄语等十余种语言的对应章节,便于不同语言背景的读者对照学习。

小结

strictNullChecks是 TypeScript 严格模式中最具“行为改变力”的开关:开启后,nullundefined从可随意赋值的“隐形值”变为必须显式声明才能出现的“一等类型”,其行为类比void;关闭时则退化为类似never的宽松语义。配合!?.??以及控制流收窄,开发者可以在编译期获得完整的空值追踪能力,将大量运行时崩溃前置为编译错误——这正是 TypeScript 相比纯 JavaScript 的核心价值之一。

  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载
上一篇:vim-airline会话自动命名规则:自定义格式
下一篇:Argo CD如何重构企业级多团队Kubernetes部署安全边界?

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询