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 ,欢迎关注,获取最新最全最深入的技术讲解

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

相关推荐
正小安29 分钟前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch2 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇3 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho4 小时前
【TypeScript】知识点梳理(三)
前端·typescript