TypeScript 中 never 类型的妙用

TypeScript 是一种静态类型的编程语言,它可以让我们在编写 JavaScript 代码时,提前发现并避免一些潜在的错误。

但是,有时候我们在修改类型时,可能会忘记修改一些相关的代码,导致类型不匹配或者缺少分支处理等问题。

有没有一种方法,可以让我们在修改类型时更加轻松和精确呢?

答案是肯定的!

我是渡一子辰老师,今天我来向你介绍一个重要的类型------never 类型。

它是 TypeScript 中最小的类型,它表示一个永远不会出现的值。

子辰还将告诉你如何利用 never 类型来确保类型约束的精确性,避免在类型修改过程中导致的错误。

什么是 never 类型

在 TypeScript 中,never 类型表示一个永远不会出现的值。

它是所有类型的子类型,也就是说,任何类型都可以赋值给 never 类型,但是 never 类型不能赋值给任何类型,当然除了它自己。

never 类型有什么用呢?我们可以用它来表示一些永远不会发生的情况,比如:

  • 抛出异常的函数
  • 死循环的函数
  • 永远不会进入的分支

例如:

js 复制代码
// 抛出异常的函数
function error(message: string): never {
  throw new Error(message);
}

// 死循环的函数
function loop(): never {
  while (true) {}
}

// 永远不会进入的分支
function foo(x: string | number) {
  if (typeof x === "string") {
  // do something
  } else if (typeof x === "number") {
  // do something else
  } else {
  // x is never
  const n: never = x;
  }
}

在上面的例子中,error 函数和 loop 函数都永远不会返回任何值,所以它们的返回类型是 never 。

foo 函数中,如果 x 是 string 或者 number ,就会进入相应的分支处理;如果 x 不是这两种类型,就会进入最后一个分支,但是这个分支永远不会进入,因为 x 的类型已经被收缩为 never 了。

所以我们可以用 never 类型来标记这个分支,并且把 x 赋值给一个 never 类型的变量 n ,这样就可以确保这个分支永远不会被执行。

never 的妙用

我们已经知道了 never 类型可以表示一些永远不会发生的情况,那么我们如何利用它来确保类型约束的精确性呢?

我们来看一个例子。

假设我们要封装一个 Ajax 请求的方法。

ts 复制代码
// 定义一个类型
type Method = 'GET' | 'POST'
// 封装一个 Ajax 请求的方法,为两个参数约束类型
function request(method: Method, url: string) { }

可以看到方法已经写好了,并且在调用 request 时也有提示了。

那么我们再来写函数里边的东西。

ts 复制代码
type Method = 'GET' | 'POST'
function request(method: Method, url: string) {
  // 因为有多种请求类型,我们这里用 switch
  // 每一个类型随便给它返回一个值仅做模拟
  switch (method) {
    case "GET":
      return "get"
    case "POST":
      return "post"
    default:
      return 'default'
  }
}

可以看到函数里边用 switch 来根据不同的请求类型返回不同的值。

但是这里有一个问题:我们定义了 Method 类型只有两种可能:GET 和 POST ,那么为什么还要写 default 分支呢?因为这个分支永远不会进入!

我们看一下 default 分支里是什么类型。

可以看到,它变成了 never 类型,这就得益于 TS 里的类型收缩,就是 TS 他会分析你的分支,根据你分支的条件来对类型做一个相应的收缩。

比如在下图的分支里,就被收缩成了 GET 值,它就不在是联合类型了。

那么在 default 里由于这个分支永远不会进来,所以说它就用类型 never 来表示了。

那么这个对我们实际开发到底有什么用呢?

我们考虑这么一种情况,如果我们不写 default 分支,我们对 Method 类型扩展了其他可能性,比如:PUT 或者 DELETE 等。

你看,在下图中我们加了一个 PUT 类型以后,并没有报错,你压根就不知道这个函数里还应该加一个 PUT 分支。

当你的代码写多了,然后再非常繁杂的代码里边,改了一个类型,你就很难知道,你这个改动会影响到哪些地方。

就是其他地方的代码到底哪些地方会做相应的修改,你是很难知道的。

当然这个代码很少,一眼就能看出来,那如果是上万行代码呢?因为 TS 往往是在大型项目工程中使用的,几万行代码你还能清楚的知道吗?

这个时候呢,我们就可以借助 never 了,来看下怎么做。

ts 复制代码
type Method = 'GET' | 'POST'
function request(method: Method, url: string) {
  switch (method) {
    case "GET":
      return "get"
    case "POST":
      return "post"
    default:
      const n: never = method;
      return n
  }
}

我们回到最初的情况,我们就加一个 default 分支,分支里定一个变量 n,标注为 never 类型,然后吧 method 赋值给它,然后返回这个 n。

它不会影响代码的执行,因为这个分支永远进不来。

而且这个赋值也是安全的,因为现在 default 分支里的 method 是 never 类型,never 是可以赋值给 never 的,但是其他类型就不能赋值给 never 了。

接下来我们就可以放心大胆的去修改类型了。

当我们加了一个 PUT 以后,就会发现报错了,因为 TS 的类型收缩,它知道分支里还有一种情况,就是 PUT,那么这种类型是不能赋值给 never 的,于是这里就报错了。

这样子呢,你就非常清楚这个地方还需要去增加一个分支,要做相应的修改。

比如说有很多这个函数,根据报错,就能很清楚的知道哪些地方要做修改了。

这是 never 类型在 TS 里边非常常见的一种做法,这种做法在大型项目里边及其有用。

总结

本文主要就是帮助你理解 never 类型在 TypeScript 中的应用及其优势。

在 TypeScript 中,never 类型表示一个永远不会出现的值,它可以在类型收缩时进行自动推导,使代码更加稳定可靠。

在函数中,我们可以利用 never 类型来确保类型约束的精确性,避免了在类型修改过程中导致的错误。特别是在大型项目中,never 类型可以帮助我们更好地维护代码,减少因修改类型而导致代码修改和错误的风险。

例如,我们文章中的例子,通过分析代码分支,我们可以使用 never 类型来标记永远不会进入的分支。这样一来,我们在以对类型进行扩展时,就可以很快地知道在哪些地方需要进行相应的修改,从而提高代码的可维护性和稳定性。

在项目开发中,特别是大型项目开发中,掌握使用 never 类型的技巧,可以让同学们更加高效、精准地编写出高质量的代码。

本文来源

本文来源自渡一官方公众号:Duing ,欢迎关注,获取最新最全最深入的技术讲解

感谢你阅读本文,如果你有任何疑问或建议,请在评论区留言,如果你觉得这篇文章有用,请点赞收藏或分享给你的朋友

相关推荐
嘤嘤怪呆呆狗5 分钟前
【插件】vscode Todo Tree 简介和使用方法
前端·ide·vue.js·vscode·编辑器
大今野10 分钟前
node.js和js
开发语言·javascript·node.js
ᥬ 小月亮17 分钟前
Js前端模块化规范及其产品
开发语言·前端·javascript
码小瑞32 分钟前
某些iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题
前端·javascript·vue.js
weixin_18934 分钟前
‌Vite和Webpack区别 及 优劣势
前端·webpack·vue·vite
半吊子伯爵35 分钟前
开发过程优化·自定义鼠标右键菜单
前端·javascript·自定义鼠标右键菜单
xcLeigh39 分钟前
HTML5实现好看的喜庆圣诞节网站源码
前端·html·html5
Tirzano1 小时前
vue3 ts 简单动态表单 和表格
前端·javascript·vue.js
杰~JIE1 小时前
前端工程化概述(初版)
前端·自动化·工程化·前端工程化·sop
程序员_三木1 小时前
使用 Three.js 创建圣诞树场景
开发语言·前端·javascript·ecmascript·three