TypeScript - Never: 理解最底层类型

你好,有抱负的程序设计师们!今天,我们将深入探讨TypeScript中一个较为神秘的类型:never类型。如果你是编程新手,不用担心——我会一步一步地引导你理解这个概念,就像我在多年的教学时间里对无数学生所做的那样。所以,拿起你最喜欢的饮料,让我们一起踏上这场激动人心的TypeScript世界之旅!

TypeScript - Never

什么是never类型?

TypeScript中的never类型通常被称为"最底层类型"或"空类型"。它表示一个永远不会发生的类型。现在,你可能在想:“我们为什么需要一个永远不会发生的类型?”我的好奇朋友们,它的用途比你想的要多!

何时使用never?

  1. 表示不可能的情况
  2. 处理穷尽性检查
  3. 在永远不会返回的函数中

让我们来看一些例子,使这些概念更加清晰。

示例 1:表示不可能的情况

function throwError(message: string): never {
throw new Error(message);
}

let result = throwError("Oops! Something went wrong!");
console.log(result); // 这行代码永远不会被执行

在这个例子中,throwError函数保证会抛出一个错误,而永远不会正常返回。因此,它的返回类型是never

可以这样理解:如果你在烤蛋糕时,食谱上写着“烤到永远”,你就知道蛋糕不会从烤箱里拿出来!

示例 2:穷尽性检查

type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * Math.pow(5, 2);
case "square":
return 10 * 10;
case "triangle":
return (10 * 5) / 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}

在这里,never帮助我们确保我们已经涵盖了所有可能的形状。如果我们向Shape类型中添加了一个新的形状,但在getArea中忘记为它添加一个case,TypeScript会给我们一个错误。就像有一个贴心的助手,在你忘记某些事情时提醒你!

示例 3:永远不会返回的函数

function infiniteLoop(): never {
while (true) {
console.log("This loop never ends!");
}
}

这个函数会永远运行(或者直到你的计算机内存耗尽)。因为它永远不会完成执行,所以它的返回类型是never。就像告诉你的朋友你会“永远”停止说话——他们知道他们将进行一场漫长的对话!

never类型与void类型的区别

现在,你可能想知道,“nevervoid有什么区别?”这是个好问题!让我们来分析一下。

void

void类型用于当函数不返回任何值时,但它确实完成了执行。

function logMessage(message: string): void {
console.log(message);
}

logMessage("Hello, TypeScript!"); // 这个函数返回undefined

never

另一方面,never类型用于当函数永远不会完成执行或总是抛出错误时。

function failwithError(message: string): never {
throw new Error(message);
}

failwithError("This function never returns!");

可以这样理解:void就像去商店然后空手而归,而never就像踏上没有目的地的旅程——你永远不会回来!

never的实际用途

让我们看看一些never可以派上用场的更多实际示例。

示例 4:类型守卫

type Square = { kind: "square", size: number };
type Circle = { kind: "circle", radius: number };
type Shape = Square | Circle;

function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}

function getArea(shape: Shape) {
switch (shape.kind) {
case "square": return shape.size * shape.size;
case "circle": return Math.PI * shape.radius ** 2;
default: return assertNever(shape); // 如果shape既不是Square也不是Circle,则报错
}
}

在这个例子中,assertNever帮助我们捕获任何可能遗漏的情况。就像在学习杂耍时有一个安全网!

示例 5:不可达代码检测

function neverReaches(): never {
while (true) {
// 一些操作
}
console.log("This line will never be reached");  // TypeScript错误
}

TypeScript足够智能,知道console.log语句永远不会被执行,并会给出错误。就像有一个GPS在你试图开车去一个不存在的地方时提醒你!

never的方法和属性

现在,你可能想知道never是否有任何方法或属性。事实是,never没有自己的方法或属性,因为它表示一个永远不会发生的类型。然而,它仍然是TypeScript类型系统的重要组成部分。

下面是一个总结你可以在never上进行的操作(或不能进行的操作)的表格:

操作 结果 说明
赋值给never ✅ 允许 任何类型都可以赋值给never
never赋值给其他类型 ❌ 不允许 never不能赋值给任何其他类型
never上调用方法 ❌ 不允许 由于never应该永远不会发生,你不能在它上面调用方法
在联合类型中使用never ✅ 允许但无效果 never在联合类型中被忽略
在交叉类型中使用never ✅ 允许并结果为never 任何类型与never交叉都会得到never

结论

亲爱的学生们,以上就是我们对never的探索,了解了它的方方面面。记住,never就像那个总是取消计划的朋友——他们永远不会出现,但仍然需要记住他们!

理解never可能一开始看起来有点棘手,但经过练习,你会发现它是你TypeScript工具箱中一个非常有价值的工具。它帮助你的代码更健壮,捕获潜在的错误,甚至让你更深入地思考你的函数行为。

继续编码,继续学习,永远不要拒绝尝试TypeScript中的新事物!下次见,愿你的编译错误少之又少,类型推断强大无比!

Credits: Image by storyset