TypeScript 特殊类型:never、void、unknown、any
TypeScript 有四个特殊的类型:never、void、unknown 和 any。
它们在类型系统中扮演重要角色,理解它们的区别对于编写类型安全的代码至关重要。
为什么需要特殊类型
TypeScript 的类型系统非常强大,除了常规的类型外,还提供了四个特殊类型来处理特定的场景。
理解这四种特殊类型的区别,能够帮助我们编写更安全、更准确的类型代码。
概念说明:never、void、unknown、any 是 TypeScript 的特殊类型,它们各自有不同的含义和使用场景。
never 类型
never 表示永不返回的类型。通常用于抛出异常或无限循环的函数。
实例
// 函数永远不会正常返回,总会抛出错误
function throwError(message: string): never {
throw new Error(message);
}
// 无限循环的函数
// 函数永远不会返回,程序会一直运行
function infiniteLoop(): never {
while (true) {
console.log("运行中...");
}
}
// never 是所有类型的子类型
// 这意味着 never 可以赋值给任何类型
var neverValue: never;
var num: number = neverValue; // 正确:never 是 number 的子类型
console.log("never 赋值给 number: " + num);
运行结果:
never 赋值给 number: undefined
重要:never 是所有类型的子类型,这意味着 never 可以赋值给任何类型,但任何类型都不能赋值给 never(除了 never 本身)。
void 类型
void 表示没有返回值,通常用于声明没有 return 语句的函数。
实例
// 函数执行后没有返回值
function logMessage(message: string): void {
console.log("日志: " + message);
// 没有 return 语句,或 return undefined
}
logMessage("Hello");
// void 变量(很少使用)
// 只能赋值为 undefined
var empty: void = undefined;
console.log("void 变量: " + empty);
运行结果:
日志: Hello void 变量: undefined
说明:void 实际上和 undefined 很相似,主要用于函数的返回类型声明,表示这个函数没有返回值。
unknown 类型
unknown 是类型安全的 any。使用 unknown 时,必须先进行类型检查才能使用。
实例
// 这是"安全的" any
var value: unknown = "hello";
value = 42;
value = true;
// 未知类型不能直接赋值给其他类型
// 如果取消注释下一行,编译器会报错
// var str: string = value;
// 需要进行类型检查后,才能赋值
if (typeof value === "string") {
// TypeScript 知道 value 是 string 类型
var str: string = value;
console.log("字符串长度: " + str.length);
}
运行结果:
字符串长度: 5
安全:unknown 强制要求在使用前进行类型检查,这保护了类型安全。处理未知来源的数据时,应该使用 unknown 而不是 any。
any 类型
any 完全绕过类型检查,可以赋值为任意类型。这是 TypeScript 中最不安全的类型。
实例
// 完全绕过类型检查
var anything: any = "hello";
anything = 42;
anything = true;
// any 可以赋值给任意类型
// 这会破坏类型安全
var str: string = anything;
var num: number = anything;
console.log("字符串: " + str);
console.log("数字: " + num);
// any 类型的对象可以调用任意方法
// 编译器不会报错,但运行时可能出错
var obj: any = {};
obj.foo(); // 不会报错,但实际上 foo 方法不存在
obj.bar = "value";
警告:any 类型会完全绕过 TypeScript 的类型检查,应该尽量避免使用。在必须使用的情况下,要确保值的类型是正确的。
对比 any vs unknown
any 和 unknown 都可以接受任意类型,但安全性完全不同。
| 特性 | any | unknown |
|---|---|---|
| 接受任意类型 | ✓ 是 | ✓ 是 |
| 直接赋值给其他类型 | ✓ 是 | ✗ 否(需检查) |
| 直接调用方法 | ✓ 是 | ✗ 否(需检查) |
| 类型安全 | ✗ 无 | ✓ 有 |
建议:处理未知类型的数据时,优先使用 unknown 而不是 any,这样能强制进行类型检查。
never vs void
never 和 void 都用于函数返回类型,但含义完全不同。
| 特性 | never | void |
|---|---|---|
| 含义 | 永不返回 | 没有返回值 |
| 用于函数 | 抛出异常/无限循环 | 普通无返回值函数 |
| 可赋值给其他类型 | ✓ 是(子类型) | 只能赋值给 void/any |
关键区别:never 表示函数永远不会正常返回(要么抛异常,要么死循环);void 表示函数正常执行完毕,但没有返回值。
实际应用:Exhaustive Check
never 类型的一个重要用途是进行穷举检查,确保所有可能的情况都被处理。
实例
type Shape = { kind: "circle", radius: number }
| { kind: "square", side: number };
// 计算形状面积
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.side ** 2;
default:
// default 分支应该处理所有未预料到的情况
// 如果添加新的 Shape 类型但忘记处理,编译器会报错
var _exhaustive: never = shape;
return _exhaustive;
}
}
var circle = { kind: "circle" as const, radius: 5 };
var square = { kind: "square" as const, side: 4 };
console.log("圆形面积: " + area(circle).toFixed(2));
console.log("正方形面积: " + area(square));
运行结果:
圆形面积: 78.54 正方形面积: 16
Exhaustive Check:这种模式确保当添加新的类型分支时,如果忘记在 switch 中处理,编译器会报错,因为新增的类型无法赋值给 never。
注意事项
- never 是子类型:never 可以赋值给任何类型,但任何类型都不能赋值给 never
- void vs undefined:在 JavaScript 中,void 和 undefined 基本等同
- unknown 需要检查:使用 unknown 类型的值前,必须进行类型检查
- 避免 any:尽量不要使用 any,优先使用 unknown
最佳实践:从高到低的类型安全程度:never > void > unknown > any。尽量使用更安全的类型。
总结
理解这四种特殊类型对于编写高质量的 TypeScript 代码非常重要。
- never:永不返回,用于 exhaustive check,是所有类型的子类型
- void:无返回值,用于普通没有返回值的函数
- unknown:安全的任意类型,使用前必须进行类型检查
- any:绕过所有类型检查,应该尽量避免使用
建议:编写代码时,优先使用更严格的类型。处理未知数据使用 unknown,函数无返回值使用 void,需要穷举检查时使用 never。
