本文献给:
已掌握 TypeScript 基础类型(string、number、boolean、数组、元组、枚举)的开发者。本文将带你探索 TypeScript 中更特殊的类型,理解它们的设计初衷与使用场景,并掌握处理空值的安全技巧。
你将学到:
any的弊端与unknown的安全用法void与never的区别及适用场景null和undefined在strictNullChecks下的行为- 可选链
?.与空值合并??的使用 - 非空断言
!的风险与正确使用方式
目录
- [一、any ------ 危险的逃生舱](#一、any —— 危险的逃生舱)
-
- [1.1 什么是 any?](#1.1 什么是 any?)
- [1.2 any 的弊端](#1.2 any 的弊端)
- [1.3 何时可以容忍 any?](#1.3 何时可以容忍 any?)
- [二、unknown ------ 安全的 any](#二、unknown —— 安全的 any)
-
- [2.1 unknown 的特性](#2.1 unknown 的特性)
- [2.2 unknown 的使用场景](#2.2 unknown 的使用场景)
- [2.3 unknown 与 any 对比](#2.3 unknown 与 any 对比)
- [三、void 与 never ------ 函数的"无返回值"](#三、void 与 never —— 函数的“无返回值”)
-
- [3.1 void ------ 没有返回值的函数](#3.1 void —— 没有返回值的函数)
- [3.2 never ------ 永远不会返回的函数](#3.2 never —— 永远不会返回的函数)
- [3.3 void 与 never 的区别](#3.3 void 与 never 的区别)
-
- [never 在类型守卫中的妙用](#never 在类型守卫中的妙用)
- [四、null 与 undefined ------ 空值处理](#四、null 与 undefined —— 空值处理)
-
- [4.1 严格空值检查(strictNullChecks)](#4.1 严格空值检查(strictNullChecks))
- [4.2 联合类型与空值](#4.2 联合类型与空值)
- [4.3 可选链(Optional Chaining)`?.`](#4.3 可选链(Optional Chaining)
?.) - [4.4 空值合并(Nullish Coalescing)`??`](#4.4 空值合并(Nullish Coalescing)
??) - [4.5 非空断言(Non-null Assertion)`!`](#4.5 非空断言(Non-null Assertion)
!) -
- [⚠️ 风险](#⚠️ 风险)
- [4.6 空值处理最佳实践](#4.6 空值处理最佳实践)
- 五、常见错误与注意事项
-
- [5.1 误用 any 导致类型失效](#5.1 误用 any 导致类型失效)
- [5.2 将 void 与 undefined 混用](#5.2 将 void 与 undefined 混用)
- [5.3 非空断言滥用](#5.3 非空断言滥用)
- [5.4 可选链与空值合并的混用陷阱](#5.4 可选链与空值合并的混用陷阱)
- 六、综合示例
- 七、小结
一、any ------ 危险的逃生舱
1.1 什么是 any?
any 是 TypeScript 中的顶级类型 ,可以表示任意值。被标记为 any 的变量会关闭所有类型检查,相当于回到了 JavaScript 的动态类型。
typescript
let value: any = "hello";
value = 42; // OK
value = true; // OK
value.toUpperCase(); // OK(运行时可能出错)
1.2 any 的弊端
any 虽然灵活,但会破坏 TypeScript 的类型安全保障:
typescript
let data: any = { name: "Alice" };
console.log(data.age.toFixed()); // ❌ 编译通过,运行时报错(Cannot read property 'toFixed' of undefined)
any 还会污染 其他类型,因为任何赋值都会导致接收方也变成 any 或失去精确类型:
typescript
let dangerous: any = "text";
let safe: number = dangerous; // 不报错,但 safe 实际是 string,后续使用可能出问题
1.3 何时可以容忍 any?
- 快速原型开发或迁移旧代码时临时使用
- 第三方库没有类型定义且无法立即补全时
- 某些极其复杂的动态场景,类型难以表达
尽量用
unknown替代any,保持类型安全。
二、unknown ------ 安全的 any
2.1 unknown 的特性
unknown 是 TypeScript 3.0 引入的安全类型 ,表示"不知道是什么类型"。与 any 不同,unknown 的值不能直接使用,必须经过类型收窄(narrowing)后才能操作。
typescript
let value: unknown = "hello";
value = 42; // OK
value = true; // OK
// 直接使用会报错
value.toUpperCase(); // ❌ 类型 unknown 没有 toUpperCase 方法
// 必须先收窄类型
if (typeof value === "string") {
console.log(value.toUpperCase()); // OK
}
2.2 unknown 的使用场景
- 从
any迁移时,先用unknown替代,强制后续收窄 - 处理 API 响应、用户输入等不确定来源的数据
- 编写通用工具函数,要求使用者显式处理类型
typescript
function safeParse(input: string): unknown {
return JSON.parse(input);
}
const result = safeParse('{"name":"Alice"}');
// 不能直接访问 result.name,需要收窄
if (typeof result === "object" && result !== null && "name" in result) {
console.log(result.name); // OK
}
2.3 unknown 与 any 对比
| 特性 | any |
unknown |
|---|---|---|
| 可赋值给任何类型 | ✅ | ❌(需先收窄) |
| 可调用方法或属性 | ✅(编译通过) | ❌(编译报错) |
| 安全性 | 低(绕过类型检查) | 高(强制类型收窄) |
| 适用场景 | 快速原型、遗留代码 | 类型未知但需安全处理 |
三、void 与 never ------ 函数的"无返回值"
3.1 void ------ 没有返回值的函数
void 表示一个函数没有返回值 ,或者返回 undefined(在严格模式下)。
typescript
function logMessage(msg: string): void {
console.log(msg);
// 没有 return,或 return; 或 return undefined;
}
变量也可以声明为 void,但只能赋值为 undefined 或 null(非严格模式):
typescript
let unusable: void = undefined;
unusable = null; // 仅在 strictNullChecks: false 时允许
注意 :在回调函数中,如果期望函数不返回值,应标注
void,而不是undefined(因为undefined表示必须返回undefined)。
3.2 never ------ 永远不会返回的函数
never 表示永远不会正常结束的函数,例如:
- 抛出异常(函数执行被中断)
- 无限循环
typescript
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
never 是所有类型的子类型,可以赋值给任何类型;但没有类型是 never 的子类型(除了 never 自身)。
3.3 void 与 never 的区别
| 特性 | void |
never |
|---|---|---|
| 含义 | 函数正常结束,无返回值 | 函数永远不会正常结束 |
| 可赋值的值 | undefined 或 null |
无(无法正常返回) |
| 使用场景 | 无返回值的函数(如事件处理) | 异常抛出、无限循环、类型守卫的穷尽检查 |
never 在类型守卫中的妙用
在 switch 或条件判断中,可以用 never 做穷尽检查:
typescript
type Shape = "circle" | "square";
function area(shape: Shape) {
switch (shape) {
case "circle":
return Math.PI * 1 ** 2;
case "square":
return 1 * 1;
default:
const _exhaustiveCheck: never = shape; // 如果 shape 类型被扩展,这里会报错
return _exhaustiveCheck;
}
}
四、null 与 undefined ------ 空值处理
4.1 严格空值检查(strictNullChecks)
TypeScript 的 strictNullChecks 选项(默认开启)严格区分 null、undefined 和其他类型。
- 开启后,
null和undefined只能赋值给any、unknown或它们自身的类型。 - 变量声明时如果不包含
null/undefined,则不能赋值为这些空值。
typescript
// strictNullChecks: true
let name: string = "Alice";
name = null; // ❌ 不能将 null 赋给 string
name = undefined; // ❌
let age: number | null = 30;
age = null; // OK
4.2 联合类型与空值
常见模式是用联合类型允许 null 或 undefined:
typescript
let maybeName: string | null = "Alice";
maybeName = null; // OK
let maybeAge: number | undefined = 25;
maybeAge = undefined; // OK
4.3 可选链(Optional Chaining)?.
访问深层属性时,如果中间某个属性可能为 null 或 undefined,使用 ?. 可以安全地短路返回 undefined。
typescript
interface User {
address?: {
city?: string;
};
}
let user: User = {};
// 传统方式
let city = user.address && user.address.city; // undefined
// 可选链
let city2 = user.address?.city; // undefined
可选链也支持函数调用和数组索引:
typescript
let result = obj.method?.(); // 如果 method 不存在,返回 undefined
let item = arr?.[0]; // 如果 arr 是 null/undefined,返回 undefined
4.4 空值合并(Nullish Coalescing)??
?? 运算符用于在左侧值为 null 或 undefined 时返回右侧值,与 || 不同,它不会将 0、""、false 视为假值。
typescript
let value1 = null ?? "default"; // "default"
let value2 = undefined ?? "default"; // "default"
let value3 = 0 ?? "default"; // 0(不是 null/undefined,保留原值)
let value4 = "" ?? "default"; // ""(同上)
// 对比 ||
let value5 = 0 || "default"; // "default"(|| 会将 0 视为假值)
4.5 非空断言(Non-null Assertion)!
当 TypeScript 无法确定一个值不为 null 或 undefined,但开发者确信它一定存在时,可以使用 ! 后缀断言。
typescript
let element = document.getElementById("app")!; // 断言该元素一定存在
element.innerHTML = "Hello";
// 也可以用于属性访问
interface User {
name?: string;
}
let user: User = { name: "Alice" };
let name = user.name!.toUpperCase(); // 断言 name 存在
⚠️ 风险
! 只是编译时断言,不会生成运行时检查。如果断言错误,运行时仍可能抛出 Cannot read property of undefined。应谨慎使用,优先考虑可选链或类型守卫。
4.6 空值处理最佳实践
| 场景 | 推荐方式 |
|---|---|
| 访问可能不存在的深层属性 | 可选链 ?. |
| 为 null/undefined 提供默认值 | 空值合并 ?? |
| 确信某个值非空但 TypeScript 不认可 | 先用 ?. 或类型守卫,最后才考虑 ! |
| 函数参数或返回值允许空值 | 显式使用联合类型 `string |
| 类型守卫收窄 | if (value !== null) { ... } 或 typeof |
五、常见错误与注意事项
5.1 误用 any 导致类型失效
typescript
let data: any = fetchData();
let result = data.user.name; // 无报错,但可能运行时崩溃
解决 :用 unknown + 类型守卫。
5.2 将 void 与 undefined 混用
typescript
function fn(): void {
return undefined; // OK
}
function fn2(): void {
return null; // ❌ 严格模式下不能返回 null
}
5.3 非空断言滥用
typescript
let maybeNum: number | null = Math.random() > 0.5 ? 42 : null;
let definitelyNum = maybeNum!; // 如果为 null,运行时错误
解决 :使用 if (maybeNum !== null) 或 ??。
5.4 可选链与空值合并的混用陷阱
typescript
let val = obj?.prop ?? "default";
// 如果 obj 不存在,obj?.prop 返回 undefined,触发 ??
// 如果 obj.prop 存在但值为 null,也会触发 ??
六、综合示例
typescript
// 模拟 API 响应
interface ApiResponse<T> {
data?: T;
error?: string;
}
// 处理不确定的响应
async function fetchUser(): Promise<ApiResponse<{ name: string }>> {
// 模拟随机成功或失败
const success = Math.random() > 0.5;
if (success) {
return { data: { name: "Alice" } };
} else {
return { error: "Network error" };
}
}
async function handleUser() {
const response = await fetchUser();
// 使用 unknown 安全处理
const unknownData: unknown = response.data;
// 类型收窄
if (unknownData && typeof unknownData === "object" && "name" in unknownData) {
console.log(unknownData.name); // 类型推断为 string
} else {
console.log(response.error ?? "Unknown error");
}
// 可选链与空值合并示例
const userName = response.data?.name ?? "Guest";
console.log(`User: ${userName}`);
}
// never 示例:类型守卫穷尽检查
type Status = "pending" | "success" | "error";
function getStatusMessage(status: Status): string {
switch (status) {
case "pending":
return "Loading...";
case "success":
return "Success!";
case "error":
return "Failed.";
default:
const _exhaustive: never = status; // 如果 Status 增加新值,这里会报错
return _exhaustive;
}
}
七、小结
| 类型 | 含义 | 使用场景 |
|---|---|---|
any |
任意类型,关闭检查 | 临时逃生舱,尽量少用 |
unknown |
未知类型,强制收窄 | 处理不确定数据,更安全 |
void |
无返回值 | 无返回值的函数 |
never |
永不返回 | 异常、无限循环、穷尽检查 |
null |
空值 | 明确表示"没有值" |
undefined |
未定义 | 未初始化的变量、缺失属性 |
?. |
可选链 | 安全访问深层属性 |
?? |
空值合并 | 为 null/undefined 提供默认值 |
! |
非空断言 | 确信值非空时使用,需谨慎 |
觉得文章有帮助?别忘了:
👍 点赞 👍 -- 给我一点鼓励
⭐ 收藏 ⭐ -- 方便以后查看
🔔 关注 🔔 -- 获取更新通知
标签: #TypeScript #空值处理 #前端开发