TypeScript 中 null 与 undefined 的区别 —— 一篇彻底搞懂的指南

在前端开发中,nullundefined 常常被混用,但在 TypeScript 的严格类型体系下,它们有着本质的不同。本文将带你全面理解这两个"空值"的区别、使用场景以及如何在 TypeScript 中安全地处理它们。

📌 目录

  • 从 JavaScript 说起
  • TypeScript 中的类型差异
  • strictNullChecks 严格空值检查
  • 两者在函数中的行为差异
  • 可选链与空值合并运算符
  • 实际开发中的使用建议
  • 总结

从 JavaScript 说起

在原生 JavaScript 中,nullundefined 都表示"空"或"没有值",但它们的起源不同:

  • undefined :表示"未定义"。当变量声明了但未被赋值,或者访问对象不存在的属性时,JavaScript 引擎会自动返回 undefined
  • null:表示"空对象指针",通常由开发者主动赋值,意为"此处应该有一个对象,但目前没有"。
javascript 复制代码
let a;               // a 的值为 undefined
let b = null;       // b 的值为 null
console.log(typeof a); // "undefined"
console.log(typeof b); // "object"  (这是一个众所周的 Bug)

二者在宽松相等 (==) 比较时为 true,但在严格相等 (===) 下为 false

ini 复制代码
null == undefined  // true
null === undefined // false

这种模糊性让很多开发者感到困惑,于是 TypeScript 提供了更严格的类型管控。

TypeScript 中的类型差异

在 TypeScript 中,nullundefined 是各自独立的类型:

ini 复制代码
let u: undefined = undefined;
let n: null = null;
  • undefined 类型只有一个值:undefined
  • null 类型只有一个值:null

如果你尝试将 null 赋给一个声明为 undefined 类型的变量,或者反过来,TypeScript 会报错(在开启严格模式时):

javascript 复制代码
let u: undefined = null;  // 错误:不能将类型"null"分配给类型"undefined"
let n: null = undefined;  // 错误:不能将类型"undefined"分配给类型"null"

另外,在 TypeScript 中,如果你声明一个变量但不赋值,它的类型会被推断为 any,而不是 undefined? 实际上要分情况:

typescript 复制代码
let x;          // 类型推断为 any
let y: number;  // 类型为 number,但未赋值时会报错(strict 下)

更常见的场景是:在 strictNullChecks 模式下,nullundefined 不能随意赋值给其他类型。

strictNullChecks ------ 严格空值检查

TypeScript 有一个关键的编译选项:strictNullChecks(通常在 strict: true 下自动开启)。它彻底改变了 nullundefined 的行为。

关闭 strictNullChecks(宽松模式)

此时,nullundefined 可以被赋值给任何类型的变量,类似于 JavaScript 的灵活行为,但这就失去了类型检查的意义。

typescript 复制代码
// 宽松模式:不报错
let name: string = null;
let age: number = undefined;

开启 strictNullChecks(推荐)

nullundefined 成为了独立的"非空"类型的禁区。以下写法会报错:

typescript 复制代码
let name: string = null;        // 错误:不能将 null 赋给 string
let age: number = undefined;    // 错误:不能将 undefined 赋给 number

想要表达一个变量可能为空,必须使用联合类型:

typescript 复制代码
let name: string | null = null;         // 允许
let age: number | undefined = undefined; // 允许
let address: string | null | undefined = undefined; // 允许两者

这种强制显式标注,让代码的空值处理变得清晰可追溯,极大地减少了运行时 Cannot read property of undefined 这类错误。

两者在函数中的行为差异

默认参数

当我们为函数参数提供默认值时,只有 undefined 会触发默认值,null 不会:

javascript 复制代码
function greet(name: string = "Guest") {
  console.log(`Hello, ${name}`);
}

greet(undefined); // Hello, Guest
greet(null);      // Hello, null

这体现了 undefined 在语言层面的特殊地位:它代表"缺失",而 null 代表"存在的空值"。

可选参数和可选属性

在 TypeScript 中,带有 ? 标记的参数或属性,其类型会自动被加上 undefined,而不是 null

typescript 复制代码
interface Person {
  name: string;
  age?: number;   // 类型实际是 number | undefined
}

function foo(x?: number) {  // x 类型是 number | undefined
  // ...
}

所以 TypeScript 的语言设计倾向于使用 undefined 表示"可选/缺失",而 null 需要开发者主动声明。

可选链与空值合并运算符

这两个运算符让处理 nullundefined 变得更加优雅。

可选链 ?.

当访问一个可能为 nullundefined 的对象属性时,可选链会短路返回 undefined,而不会抛出错误。

typescript

sql 复制代码
interface User {
  address?: {
    street: string;
  };
}

let user: User | null = null;
console.log(user?.address?.street); // undefined(不报错)

注意:可选链对 nullundefined 都会短路,不作区分。

空值合并运算符 ??

只有值严格为 nullundefined 时,才返回右侧的默认值。这与 || 不同(|| 会在假值 0, '', false 时也返回右侧)。

typescript

typescript 复制代码
let value: string | null | undefined;
value = null;
console.log(value ?? "default");   // "default"

value = 0;
console.log(value ?? 100);         // 0
console.log(value || 100);         // 100

因此,在需要区分"空指针"和"假值"的业务逻辑中,优先使用 ??


实际开发中的使用建议

1. 总是开启 strictNullChecks

tsconfig.json 中配置:

json

bash 复制代码
{
  "compilerOptions": {
    "strict": true          // 或者单独开启 "strictNullChecks": true
  }
}

2. 明确语义:undefined 表示"未初始化/缺失",null 表示"主动清空"

很多规范建议:

  • 对于可能尚未存在的属性/变量,用 undefined(例如可选参数、未赋值的变量)。
  • 当你想要重置一个对象引用时,显式赋值为 null,表示"曾经有,现在没有了"。

3. 避免同时使用 nullundefined

在同一个项目中最好约定统一使用一种来表示"空"。最常见的是只用 undefined 表示缺失,不得已时才用 null(例如与某些后端 API 或库交互时)。

4. 使用类型守卫缩小范围

typescript

javascript 复制代码
function process(value: string | null) {
  if (value === null) {
    // 此时 TypeScript 知道 value 是 null
    return;
  }
  // 这里 value 被收窄为 string
  console.log(value.toUpperCase());
}

5. 使用非空断言操作符 !(慎重)

当你比 TypeScript 更清楚某个值不可能为 null/undefined 时,可以使用 ! 断言,但滥用会增加运行时风险。

typescript

ini 复制代码
let elem = document.getElementById("app")!; // 认为它一定存在

总结

特性 undefined null
含义 未定义(变量未被赋值) 空值(主动设置为空)
类型 undefined null
JavaScript typeof "undefined" "object"(遗留 Bug)
默认参数触发 ✅ 会触发默认值 ❌ 不会触发,会被当作 null 传递
可选属性/参数类型 自动包含 undefined 需要显式添加 null
?? 的短路条件
推荐使用场景 表示"缺失"、"未初始化" 表示"主动清空"、"空对象指针"

TypeScript 通过严格的类型系统和编译选项,将 nullundefined 区分对待,大大提升了代码的安全性。作为开发者,我们需要理解它们各自的设计初衷,并在项目中保持统一的风格,从而写出更健壮的应用程序。

相关推荐
烛衔溟4 小时前
TypeScript 接口实战 —— 处理复杂嵌套对象
linux·ubuntu·typescript
求学中--1 天前
ArkUI电商首页完整实战
华为·typescript·harmonyos
布局呆星2 天前
Spring Boot+MyBatis-Plus+Vue3前后端协作Note
spring boot·typescript·vue·mybatis
漫游的渔夫2 天前
前端开发者做 AI Agent:别只渲染答案,用 7 个状态接住确认、错误和 trace
前端·人工智能·typescript
旷世奇才李先生3 天前
Vue3\+TypeScript 2026实战——企业级前端项目架构搭建与性能优化全指南
前端·架构·typescript
漫游的渔夫3 天前
前端开发者做 AI 工程:别停在脚本阶段,用 2 个 API 把 Agent 交给前端调用
前端·人工智能·typescript
李游Leo3 天前
TypeScript + React 全栈学习:别只背语法,先把项目链路跑通
学习·react.js·typescript
吴声子夜歌4 天前
Vue3——TypeScript基础
javascript·typescript
小p4 天前
typescript 类型体操学习2:TypeScript 类型系统支持哪些类型及类型运算?
typescript