从TypeScript教程吃透JS超集-4、TypeScript中的三种特殊类型-any_unknown_never

本篇介绍TypeScript中的三种特殊类型——any、unknown、never,我们可以通过学习这三种特殊类型来开始我们对于TypeScript的类型系统的学习。

一、any类型

any类型表示没有任何类型限制,如果一个变量被定义为any类型,则表示该变量可以被赋予任何类型的值。 例如:

let n: any;
n = 1; // valid
n = 'foo'; // valid
n = true; // valid

上述代码中,变量n被定义为any类型,那么我们对其不论是赋值为number,还是string,亦或是boolean,都是被允许的。
原则上,一旦变量被定义为any类型,则TypeScript就会关闭对这个变量的类型检查。即使有明显的错误,但只要语法正确,都不会报错。例如:

let n: any = 'hello';
n.foo = 100; // valid
n(1); // valid

我们发现,即使我们对一个any类型的变量进行了初始化赋值,并且对其赋值为字符串,但当我们尝试将其作为对象使用或者将其作为函数调用时,TypeScript仍然不会在编译时报错。原因就是变量n的类型为any,TypeScript不对其进行任何类型检查。
从集合论的角度看,any类型可以看成是所有其他类型的全集,包含了一切可能的类型。TypeScript将这种类型称为“顶层类型”(top type),意为涵盖所有下层类型。总之,TypeScript认为,只要开发者使用了any类型,就表示开发者想要自己来处理这些代码,所以就不对any类型进行任何限制,怎么使用都可以。
但这样操作在优秀的开发者的代码中显然是不被允许出现的,因为这会在运行时导致不可知的错误发生。就像在使用JavaScript代码一样。处于这个原因,我们应当在TypeScript项目中,避免使用any类型,否则便失去了使用TypeScript的意义,相对于JavaScript项目,甚至只是增加不必要的工作量。
但是遵循存在即合理的理论,any类型也有其适用的场合,例如:

  1. 出于特殊原因,需要关闭某些变量的类型检查时;
  2. 为了适配老旧的JavaScript项目,让代码快速迁移到TypeScript时;

这里我们思考一个问题,既然TypeScript提供了any类型,我们讲TypeScript是具有类型推断的能力的,那么有没有可能在TypeScript语法中,会出现推断类型any?我们看下面这个例子:

funciont add(x, y) {
  return x + y;
}
add(1, [1, 2, 3]) // valid

很显然,函数add()的两个参数都没有足够的信息使得TypeScript合理推断出他们的类型,那么TypeScript则会默认这两个参数变量的类型都为any,以至于对该函数的返回值也不再进行类型检查,直接推断其返回any类型。
这是一个很糟糕的情况,在实际的业务开发中,这样的推断会明显导致不可知错误的出现。
所以,再次强调在编写TypeScript项目代码时,对于类型不明显的变量,一定要尽可能的进行显式类型声明,防止其被推断为any类型
当然tsc编译器提供了一个编译选项noImplicitAny来阻止这个愚蠢的设定,该选项参数开启后可以使ts脚本文件在编译时一旦推断出任何any类型,就会报错!使用方法如下:
$ tsc --noImplicitAny app.ts
当然在特定情况中,该命令会失效,即:当我们使用let或者var定义变量时,既不声明类型,也不进行初始化赋值,那么在此后相关代码即使推断出any类型,使用该命令进行编译也不会出现报错
使用const定义变量不会出现这个问题,因为const在JavaScript语言中被规定声明变量时,一定要进行初始化赋值。

上面我们提到滥用any会造成一些不可知错误的产生,除此之外,我们还应当考虑非法使用any类型,大概率会导致“污染”其他变量。由于使用any类型的变量可以被赋值为任意类型的值,在变量被混合使用时,则会导致其他变量出错。例如:

let x: any = 'hello';
let y: number;
y = x; // valid

在上述代码中,我们将x变量声明为any类型并初始化赋值为字符串‘hello’,y变量声明为number类型,理论上number类型不应当接受字符串类型值的赋值操作,但我们发现在tsc编译时并不会报错提示,这样的代码在进行逻辑操作运行时,将会可能出现错误执行。
总而言之,尽可能的避免在TypeScript代码中使用any类型,这个习惯将让你的TypeScript生涯受益无穷!

二、unknown类型

上面讲到any类型会“污染”其他变量,TypeScript官方为了解决这个问题,在3.0版本中引入了unknown类型。unknown类型与any类型含义相同,表示类型不确定,可以是任意类型,但它更像是严格版的any,在使用上有一些限制
unknown类型和any类型的相同之处在于:所有的类型的值都可以直接赋值给unknown类型的变量。例如:

let uk: unknown;
uk = true; // valid
uk = 123; // valid
uk = 'hello ts'; // valid

unknown类型和any类型的不同之处在于:unknown类型的变量不能直接使用。比如:

// 禁止直接使用unknown类型变量赋值给其他类型变量(any和unknown类型除外)
let uk: unknown = 'unknown_value';
let foo: string = uk; // error
let fooAny: any = uk; // valid
let fooUnknown: unknown = uk; // valid
// 禁止直接调用unknown类型变量的属性和方法
let ukObj: unknown = { foo: 123 };
ukObj.foo; // error
let ukFun: unknown = n => n + 1;
unFun(3); // error
// unknown类型的变量只能进行比较运算(==、===、!=、!==、||、&&、?)、取反运算(!)、typeof运算和instanceof运算,进行其他运算时会报错
let n: unknown = 1;
console.log(n + 1); // error

我们应当在什么时候才能使用unknown类型?想正常使用unknown类型变量,应当先经过typeof运算判断其类型符合当前语句类型范围,确保不会出错时,才可以使用unknown类型变量的值。例如:

let num: unknown = 1;
if (typeof num === 'number') {
  let r = num + 10; // valid
}

在上述代码中,变量num虽然被定义为unknown类型,但经过“类型缩小”,通过typeof运算判断其值为number时,符合当前上下文语句的类型范围,则可将其与其他被允许的类型变量一同使用。
这样设计的目的是,只有明确unknown变量的实际类型,才允许使用它,防止像any那样可以随意乱用,“污染”其他变量。类型缩小以后再使用,就不会报错。总之,unknown可以看作是更安全的any。一般来说,凡是需要设为any类型的地方,通常都应该优先考虑设为unknown类型。
在集合论上,unknown也可以视为所有其他类型(除了any)的全集,所以它和any一样,也属于TypeScript的顶层类型。

三、never类型

never类型表示不存在任何属于该类型的值,即该类型为空,不包含任何值
当一个变量被指定为never类型,则不能给其赋于任何值,否则都会报错。
never类型的使用场景主要是一些类型运算当中,用来确保类型运算的完整性。另外,不能返回值的函数,也可以将其返回值的类型指定为never。这两点在后面会详细讲到。
需要注意一点:never类型可以赋值给其他任意类型。例如:

function fun(): never {
  throw new Error('Error Message!');
}
let eg1: number = f(); // valid
let eg2: string = f(); // valid
let eg3: boolean = f(); // valid

上述代码中,函数fun()抛出错误,我们将其返回值类型指定为never,即不返回任何值。在这种设定下,我们可以将该函数的运行结果赋值给任意类型的变量,且不会出现报错。
在集合论上,空集是任何集合的子集,TypeScript也如此规定:任何类型都包含了never类型。因此,never类型是任何其他类型所共有的,TypeScript把这种情况称为“底层类型”(bottom type)。

总结:TypeScript有两个“顶层类型”(any和unknown),但是“底层类型”只有never唯一一个
感谢阅读,欢迎关注点赞收藏转发留言讨论。

  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值