null 和 undefined 在 TypeScript 中是如何处理的?
在 TypeScript 中,null 和 undefined 的处理方式主要取决于编译选项 strictNullChecks 的设置。这是 TypeScript 类型系统中最关键的概念之一。
以下是详细的解释:
1. 基础概念
在 JavaScript 中:
undefined:表示变量已声明但尚未赋值,或者对象属性不存在。null:表示一个有意的“空值”或“无对象”。
在 TypeScript 中,null 和 undefined 既是值,也是类型。
let u: undefined = undefined;
let n: null = null;
2. strictNullChecks 编译选项
这是决定 TypeScript 如何处理这两个值的核心开关。
情况 A:strictNullChecks: false (非严格模式)
这是早期 TypeScript 的默认行为(现在不推荐)。在这种模式下,null 和 undefined 是所有类型的子类型。
这意味着你可以把 null 或 undefined 赋值给 number、string 等任何类型。
// strictNullChecks: false
let num: number;
num = 1; // OK
num = null; // OK (不会报错)
num = undefined; // OK (不会报错)
// 危险:这可能导致运行时错误 "Cannot read property ... of null"
num.toFixed(2); // 如果 num 是 null,这里运行时会炸
情况 B:strictNullChecks: true (严格模式,推荐)
在现代 TypeScript 项目中,通常都会开启此选项(或者开启 strict: true)。
在这种模式下,null 和 undefined 不再是其他类型的子类型。它们是独立的类型。
// strictNullChecks: true
let num: number;
num = 1; // OK
num = null; // Error: Type 'null' is not assignable to type 'number'.
num = undefined; // Error: Type 'undefined' is not assignable to type 'number'.
如果你想让一个变量既可以是数字也可以是空,必须显式使用联合类型 (Union Types):
let num: number | null;
num = 1; // OK
num = null; // OK
3. 常用操作符与语法
在严格模式下处理 null 和 undefined 时,TypeScript 提供了一系列工具来简化代码:
A. 可选参数与属性 (?)
在函数参数或接口属性后加 ?,会自动包含 | undefined(注意:通常不包含 null,除非显式声明)。
function printName(name?: string) {
// name 的类型是 string | undefined
console.log(name);
}
interface User {
id: number;
email?: string; // 类型是 string | undefined
}
B. 可选链操作符 (Optional Chaining ?.)
安全地访问可能为 null 或 undefined 的对象属性。
let user: User | null = null;
// 如果 user 是 null,代码不会崩溃,而是返回 undefined
const email = user?.email;
C. 空值合并操作符 (Nullish Coalescing ??)
当左侧操作数为 null 或 undefined 时,返回右侧的操作数。这比 || 更安全(|| 会把 0 或 "" 也视为假值)。
const input = null;
const value = input ?? "Default Value"; // 结果: "Default Value"
const count = 0;
const validCount = count ?? 100; // 结果: 0 (如果是 count || 100,结果会是 100)
D. 非空断言操作符 (Non-null Assertion !)
当你确定某个变量此时一定不是 null 或 undefined,但 TypeScript 无法自动推断出来时,可以使用 ! 告诉编译器“相信我,它有值”。
const myString: string | null = "Hello";
// 编译器可能认为它是 null,但如果你确定它不是:
const length = myString!.length;
注意:慎用 !,因为它会绕过类型检查,如果运行时真的是 null,程序会崩溃。
4. 类型收窄 (Type Narrowing)
在使用联合类型(如 string | null)时,你需要先检查它不是 null,才能像使用 string 一样使用它。这叫“类型收窄”。
function doSomething(value: string | null) {
// value.toUpperCase(); // Error: value 可能为 null
if (value !== null) {
// 在这个块级作用域内,TS 知道 value 一定是 string
console.log(value.toUpperCase());
}
}
5. void vs undefined
undefined: 是一个具体的类型和值,表示变量未定义。void: 专门用于表示函数没有返回值。
function logMessage(): void {
console.log("Hi");
// 实际上返回的是 JavaScript 的 undefined,但在 TS 类型系统中它叫 void
}
总结
- 始终开启
strictNullChecks(或strict模式)。这是编写健壮 TypeScript 代码的基础。 - 如果一个值可能不存在,显式声明为
Type | null或Type | undefined。 - 利用
?.和??来优雅地处理空值。 - 利用 类型收窄 (if check) 来安全地使用可能为空的变量。