TypeScript 教程 第9章:null 与 undefined 处理

📘 TypeScript 教程 第9章:nullundefined 处理


一、nullundefined 的基本概念

在 JavaScript 中,nullundefined 是两个特殊的值,表示“无”或“未定义”。

类型 含义
undefined 变量未赋值时的默认值,表示“尚未赋值”
null 表示“空值”,通常用于显式表示一个变量没有指向任何对象或值
let a: undefined = undefined;
let b: null = null;

二、--strictNullChecks 的作用(核心机制)

TypeScript 提供了编译选项 --strictNullChecks,它是严格类型检查的一部分。

✅ 开启 --strictNullChecks 后的行为:

  • nullundefined 不能赋值给其他类型
  • 需要使用联合类型来处理可能为空的情况;
  • 提高类型安全性,防止运行时错误。
let name: string = null; // ❌ 报错:Type 'null' is not assignable to type 'string'

✅ 正确写法(使用联合类型):

let name: string | null = null; // ✅ 合法

⚠️ 关闭 --strictNullChecks 的行为:

  • nullundefined 可以赋值给任意类型;
  • 类型系统更宽松,但容易引发运行时错误。

三、联合类型处理空值(T | null | undefined

联合类型是处理空值的标准方式。

✅ 常见用法:

function printName(name: string | null): void {
  if (name === null) {
    console.log("Name is missing");
  } else {
    console.log(`Hello, ${name}`);
  }
}

🧠 更复杂的联合类型:

type Maybe<T> = T | null | undefined;

function processUser(user: Maybe<{ id: number; name: string }>): void {
  if (user) {
    console.log(`Processing user: ${user.name}`);
  } else {
    console.log("No user provided");
  }
}

四、非空断言操作符(Non-null Assertion Operator)!

当你确定某个变量不可能为 nullundefined 时,可以使用非空断言操作符 ! 来告诉 TypeScript 不需要进行空值检查。

✅ 示例:

let value: string | null = "hello";
console.log(value!.toUpperCase()); // ✅ 安全地调用方法

⚠️ 使用风险:

如果变量实际为 nullundefined,会导致运行时错误。

let value: string | null = null;
console.log(value!.length); // ❗ 运行时报错:Cannot read property 'length' of null

五、可选属性与 undefined

接口中使用 ? 标记属性为可选,其类型自动包含 undefined

✅ 示例:

interface User {
  id: number;
  name: string;
  email?: string; // 等价于 email: string | undefined
}

let user: User = {
  id: 1,
  name: "Alice"
};

六、安全访问嵌套对象属性(Optional Chaining)

使用 ?. 操作符可以安全访问嵌套对象属性。

✅ 示例:

interface Product {
  id: number;
  details?: {
    name: string;
    price: number;
  };
}

let product: Product = { id: 1 };

console.log(product.details?.price); // 输出: undefined,不会报错

七、空值合并运算符(Nullish Coalescing)??

用于提供默认值,仅当左侧为 nullundefined 时才使用右侧值。

✅ 示例:

let input: string | null = null;
let result = input ?? "default"; // 输出: "default"

❗ 与 || 的区别:

let input = "";
let result1 = input || "default";  // 输出: "default" (因为 "" 被视为 falsy)
let result2 = input ?? "default";  // 输出: "" (因为 input 不是 null/undefined)

八、类型守卫(Type Guards)与空值判断

✅ 使用条件判断:

function getLength(str: string | null): number {
  if (str === null) {
    return 0;
  }
  return str.length;
}

✅ 自定义类型守卫函数:

function isNotNull<T>(value: T | null | undefined): value is T {
  return value !== null && value !== undefined;
}

let data: string | null = "hello";
if (isNotNull(data)) {
  console.log(data.toUpperCase());
}

九、综合实战示例

// 定义一个泛型类型 ApiResponse<T>,用于表示 API 响应
type ApiResponse<T> = {
  status: 'success' | 'error';  // 状态可以是 'success' 或 'error'
  data?: T | null;              // 可选的数据字段,类型为 T 或 null
  message?: string;             // 可选的错误消息字段
};

// 定义一个函数 fetchUserInfo,用于模拟获取用户信息的 API 请求
function fetchUserInfo(): ApiResponse<{ name: string }> {
  // 模拟 API 请求失败
  return {
    status: 'error',
    data: null,
    message: 'User not found'
  };
}

// 调用 fetchUserInfo 函数并获取响应
const response = fetchUserInfo();

// 根据响应状态处理结果
if (response.status === 'success' && response.data?.name) {
  console.log(`User name: ${response.data.name}`);
} else {
  console.error(response.message);
}

十、10道高频面试题(含详解)

Q1: --strictNullChecks 有什么作用?

答案:
开启后禁止将 nullundefined 赋值给其他类型,提高类型安全性。


Q2: 如何处理一个可能是 null 的变量?

答案:
使用联合类型 T | null,并结合类型守卫或 ! 断言。


Q3: ! 操作符的作用是什么?有什么风险?

答案:
强制告诉 TS 该变量不为空,但如果实际为空会引发运行时错误。


Q4: nullundefined 的区别是什么?

答案:

  • null 表示“有意设置为空”;
  • undefined 表示“未赋值”或“不存在”。

Q5: a ?? ba || b 的区别?

答案:

  • ?? 仅在 a == null 时返回 b
  • ||a 为 falsy 值时就返回 b

Q6: 如何定义一个可能为空的对象属性?

答案:

interface User {
  id: number;
  nickname?: string; // 等价于 string | undefined
}

Q7: 下面代码是否合法?

let value: string = null;

答案:
不合法(在 --strictNullChecks 开启的情况下),应改为:

let value: string | null = null;

Q8: 如何安全访问嵌套对象属性?

答案:
使用可选链 ?. 操作符:

obj?.prop?.nestedProp;

Q9: 如何编写一个类型守卫函数判断变量不为空?

答案:

function isDefined<T>(value: T | null | undefined): value is T {
  return value !== null && value !== undefined;
}

Q10: null 是否可以赋值给 number 类型?

答案:
不可以(在 --strictNullChecks 开启下),必须使用联合类型 number | null


十一、总结

项目 内容
核心机制 --strictNullChecks、联合类型、非空断言
最佳实践 优先使用联合类型和类型守卫,谨慎使用 !
建议 打开 --strictNullChecks,避免运行时错误