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 ,欢迎关注,获取最新 、最全 、最深入的技术讲解
感谢你阅读本文,如果你有任何疑问或建议,请在评论区留言,如果你觉得这篇文章有用,请点赞收藏或分享给你的朋友