TypeScript中never类型的核心用法与原理解析

作者:异步漫步时间:2026-10-31 21:57:18浏览:576

本文深入解析TypeScript中的never类型,阐述其在抛出异常、死循环函数及类型保护中的应用。重点介绍利用never进行 exhaustive checking 以确保switch分支全覆盖,并剖析条件类型中never返回never的原理及元组包装解决方案,帮助开发者提升代码健壮性。

在 TypeScript 语言体系中,never 代表一种特殊的类型,指代那些永远不会存在的值。它通常用于描述逻辑上不可能发生的情况,例如函数抛出异常、不返回任何值的场景,或者处理理论上永远不会到达的代码分支。

接下来将介绍其基本用法及需要注意的关键点。

1. never 的基本用法

首先,对于会抛出错误并终止执行的函数,其返回类型应明确声明为 never。

🌰:

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

其次,对于永远不会结束的函数(如无限循环),同样可以使用 never 类型进行标注。

🌰:

function infiniteLoop(): never {
  while (true) {}
}

最后,在类型保护机制中,利用 never 检查不可能的情况,能够有效提升代码的严格程度和可读性。

🌰:

function handleValue(value: string | number) {
  if (typeof value === 'string') {
    console.log('String value');
  } else if (typeof value === 'number') {
    console.log('Number value');
  } else {
    // 如果 value 类型是 never,编译器会检查这里的情况是否可能发生
    const _exhaustiveCheck: never = value;
    throw new Error('Unexpected type: ' + value);
  }
}
handleValue('string');
handleValue(1);
handleValue(true);

1. never 的基本用法

2. never 的高级应用

第一,确保类型覆盖完整:在处理 switch 语句或多类型分支时,使用 never 可以强制编译器检查是否所有可能的分支都已处理,从而帮助发现遗漏的情况。

举例说明:假设存在一个表示用户状态的联合类型 UserStatus。

type UserStatus = "active" | "inactive" | "banned";

function handleUserStatus(status: UserStatus) {
  switch (status) {
    case "active":
      console.log("User is active");
      break;
    case "inactive":
      console.log("User is inactive");
      break;
    case "banned":
      console.log("User is banned");
      break;
    default:
      // 强制类型检查是否已全面覆盖所有情况
      const exhaustiveCheck: never = status;
      throw new Error(`Unhandled status: ${status}`);
  }
}

在 switch 语句的 default 分支中,定义一个类型为 never 的变量 exhaustiveCheck,并将 status 赋值给它。TypeScript 会在编译阶段进行检查,如果 status 不是 never 类型(意味着存在未处理的状态),则会触发编译错误。

例如:若未来在 UserStatus 中新增状态 suspended,但未在 switch 语句中处理该状态,TypeScript 将报错。

type UserStatus = 'active' | 'inactive' | 'banned' | 'suspended';

1. never 的基本用法

这种方式确保了在新增状态时类型覆盖的完整性,有助于维护代码的健壮性和可读性。

第二,辅助类型收窄:never 可以帮助 TypeScript 判断哪些代码路径是不可访问的,从而优化代码检查逻辑和类型收窄过程。

🌰:

// 在类型约束中,表示参数的类型不是某一个类型,比如:类型除了 number 之外都OK
type MyExclude<T, U> = T extends U ? never : T;
// function isNumber<T>(x: T extends number ? never : T) {}
function isNumber<T>(x: MyExclude<T, number>) {}
isNumber('123')
isNumber({})
isNumber(true)
isNumber(111)

3. 判断 never 类型的方法

在 TypeScript 中,如果直接使用条件类型 T extends never ? true : false 来判断 never 类型,结果会返回 never 而非 true。

造成这种现象的原因在于:

在条件类型中,当 T 为联合类型时,TypeScript 会对 T 中的每个成员独立应用条件(即分配式条件类型)。然而,never 被视为“空的联合类型”,没有任何成员,因此 TypeScript 无法真正应用条件判断,直接返回 never。

这涉及到分配式条件类型的机制。

当条件类型遇到联合类型时,会逐一分配并进行条件判断,即所谓的分配式。由于 never 不属于任何集合,在分配式条件下,它会直接返回 never,而不会像其他类型那样返回 true 或 false。

针对此问题的解决方法如下:

为了使条件类型能够正确判断 never,可以将 T 包装在元组中,以阻止分配式条件类型的影响。

type IsNever<T> = [T] extends [never] ? true : false;

通过这种方式,强制 T 作为一个整体进行判断,因此对于 never 类型将得到 true 的结果。

🌰:

type Test1 = IsNever<never>; // true
type Test2 = IsNever<string>; // false
type Test3 = IsNever<undefined>; // false
type Test4 = IsNever<any>; // false

4. 使用注意事项

首先,避免滥用:never 仅适用于不可能有结果的情况;如果函数可能有返回值,使用 void 类型更为合适。

其次,注意与 void 的区别:void 表示函数无返回值,但仍会返回 undefined,而 never 表示函数根本不会返回。

最后,应用于类型收窄:never 可以帮助 TypeScript 识别和优化代码分支,若分支逻辑不完整,编译器会发出警告。

以上关于 TypeScript 中 never 类型的核心用法与原理解析到此结束,更多相关内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

作者最新文章
相关文章
更多