TypeScript 中的 any 类型,unknown 类型,never 类型

一、any 类型

1、基本含义

any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。

let x: any;

x = 1; // 正确
x = "foo"; // 正确
x = true; // 正确

上面示例中,变量x的类型是any,就可以被赋值为任意类型的值。

变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。

let x: any = "hello";

x(1); // 不报错
x.foo = 100; // 不报错

上面示例中,变量x的值是一个字符串,但是把它当作函数调用,或者当作对象读取任意属性,TypeScript 编译时都不报错。原因就是x的类型是any,TypeScript 不对其进行类型检查。

由于这个原因**,应该尽量避免使用any类型,否则就失去了使用 TypeScript 的意义**。

2、实际开发中,any类型主要适用以下两个场合。
  1. 出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any。
  2. 为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any,TypeScript 编译时就不会报错。

从集合论的角度看,any类型可以看成是所有其他类型的全集,包含了一切可能的类型。TypeScript 将这种类型称为“顶层类型”(top type),意为涵盖了所有下层。

3、类型推断问题

对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量,如果无法推断出类型,TypeScript 就会认为该变量的类型是any。

function add(x, y) {
  return x + y;
}

add(1, [1, 2, 3]); // 不报错

上面示例中,函数add()的参数变量x和y,都没有足够的信息,TypeScript 无法推断出它们的类型,就会认为这两个变量和函数返回值的类型都是any。以至于后面就不再对函数add()进行类型检查了,怎么用都可以。

这显然是很糟糕的情况,所以对于那些类型不明显的变量,一定要显式声明类型,防止被推断为any。

TypeScript 提供了一个编译选项noImplicitAny,打开该选项,只要推断出any类型就会报错。

$ tsc --noImplicitAny app.ts

上面命令使用了noImplicitAny编译选项进行编译,这时上面的函数add()就会报错。

这里有一个特殊情况,即使打开了noImplicitAny,使用let和var命令声明变量,但不赋值也不指定类型,是不会报错的。

var x; // 不报错
let y; // 不报错

上面示例中,变量x和y声明时没有赋值,也没有指定类型,TypeScript 会推断它们的类型为any。这时即使打开了noImplicitAny,也不会报错。

let x;

x = 123;
x = { foo: "hello" };

上面示例中,变量x的类型推断为any,但是不报错,可以顺利通过编译。

由于这个原因,建议使用let和var声明变量时,如果不赋值,就一定要显式声明类型,否则可能存在安全隐患

const命令没有这个问题,因为 JavaScript 语言规定const声明变量时,必须同时进行初始化(赋值)。

const x; // 报错

上面示例中,const命令声明的x是不能改变值的,声明时必须同时赋值,否则报错,所以它不存在类型推断为any的问题。

4、污染问题

any类型除了关闭类型检查,还有一个很大的问题,就是它会“污染”其他变量。它可以赋值给其他任何类型的变量(因为没有类型检查),导致其他变量出错

let x: any = "hello";
let y: number;

y = x; // 不报错

y * 123; // 不报错
y.toFixed(); // 不报错

上面示例中,变量x的类型是any,实际的值是一个字符串。变量y的类型是number,表示这是一个数值变量,但是它被赋值为x,这时并不会报错。然后,变量y继续进行各种数值运算,TypeScript 也检查不出错误,问题就这样留到运行时才会暴露。

污染其他具有正确类型的变量,把错误留到运行时,这就是不宜使用any类型的另一个主要原因。

二、unknown 类型

1.定义

为了解决any类型“污染”其他变量的问题,TypeScript 3.0 引入了unknown类型。它与any含义相同,表示类型不确定,可能是任意类型,但是它的使用有一些限制,不像any那样自由,可以视为严格版的any

unknown跟any的相似之处,在于所有类型的值都可以分配给unknown类型

let x: unknown;

x = true; // 正确
x = 42; // 正确
x = "Hello World"; // 正确

上面示例中,变量x的类型是unknown,可以赋值为各种类型的值。这与any的行为一致。

unknown类型跟any类型的不同之处在于,它不能直接使用

2、主要有以下几个限制。
  1. 首先,unknown类型的变量,不能直接赋值给其他类型的变量(除了any类型和unknown类型)
let v: unknown = 123;

let v1: boolean = v; // 报错
let v2: number = v; // 报错

上面示例中,变量v是unknown类型,赋值给any和unknown以外类型的变量都会报错,这就避免了污染问题,从而克服了any类型的一大缺点。

  1. 其次,不能直接调用unknown类型变量的方法和属性。
let v1: unknown = { foo: 123 };
v1.foo; // 报错

let v2: unknown = "hello";
v2.trim(); // 报错

let v3: unknown = (n = 0) => n + 1;
v3(); // 报错

上面示例中,直接调用unknown类型变量的属性和方法,或者直接当作函数执行,都会报错。

  1. 再次,unknown类型变量能够进行的运算是有限的,只能进行比较运算(运算符==、=、!=、!、||、&&、?)、取反运算(运算符!)、typeof运算符和instanceof运算符这几种,其他运算都会报错。
let a: unknown = 1;

a + 1; // 报错
a === 1; // 正确

上面示例中,unknown类型的变量a进行加法运算会报错,因为这是不允许的运算。但是,进行比较运算就是可以的。

3、那么,怎么才能使用unknown类型变量呢?

答案是只有经过“类型缩小”,unknown类型变量才可以使用。所谓“类型缩小”,就是缩小unknown变量的类型范围,确保不会出错。

let a: unknown = 1;

if (typeof a === "number") {
  let r = a + 10; // 正确
}

上面示例中,unknown类型的变量a经过typeof运算以后,能够确定实际类型是number,就能用于加法运算了。这就是“类型缩小”,即将一个不确定的类型缩小为更明确的类型。

下面是另一个例子。

let s: unknown = "hello";

if (typeof s === "string") {
  s.length; // 正确
}

上面示例中,确定变量s的类型为字符串以后,才能调用它的length属性。

这样设计的目的是,只有明确unknown变量的实际类型,才允许使用它,防止像any那样可以随意乱用,“污染”其他变量。类型缩小以后再使用,就不会报错。

总之,unknown可以看作是更安全的any。一般来说,凡是需要设为any类型的地方,通常都应该优先考虑设为unknown类型。
在集合论上,unknown也可以视为所有其他类型(除了any)的全集,所以它和any一样,也属于 TypeScript 的顶层类型。

三、never 类型

1、定义

为了保持与集合论的对应关系,以及类型运算的完整性,TypeScript 还引入了“空类型”的概念,即该类型为空,不包含任何值

由于不存在任何属于“空类型”的值,所以该类型被称为never,即不可能有这样的值。

typescript
let x: never;

上面示例中**,变量x的类型是never,就不可能赋给它任何值**,否则都会报错。

never类型的使用场景,主要是在一些类型运算之中,保证类型运算的完整性,详见后面章节。另外,不可能返回值的函数,返回值的类型就可以写成never。

如果一个变量可能有多种类型(即联合类型),通常需要使用分支处理每一种类型。这时,处理所有可能的类型之后,剩余的情况就属于never类型。

function fn(x: string | number) {
  if (typeof x === "string") {
    // ...
  } else if (typeof x === "number") {
    // ...
  } else {
    x; // never 类型
  }
}

上面示例中,参数变量x可能是字符串,也可能是数值,判断了这两种情况后,剩下的最后那个else分支里面,x就是never类型了。

2、never类型的一个重要特点是,可以赋值给任意其他类型。
typescript
function f(): never {
  throw new Error("Error");
}

let v1: number = f(); // 不报错
let v2: string = f(); // 不报错
let v3: boolean = f(); // 不报错

上面示例中,函数f()会抛错,所以返回值类型可以写成never,即不可能返回任何值。各种其他类型的变量都可以赋值为f()的运行结果(never类型)。

3、为什么never类型可以赋值给任意其他类型呢?

这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了never类型。因此,never类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。

总之,TypeScript 有两个“顶层类型”(any和unknown),但是“底层类型”只有never唯一一个。

参考链接

https://typescript.p6p.net/typescript-tutorial/any.html

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的。 TypeScript 的 `any` 类型表示一个可以是任何类型的值。它可以用于避免类型检查,或者在你还没有确定类型的情况下使用。但是,使用 `any` 类型会放弃 TypeScript 带来的类型检查的所有优势,因此在能使用更精确类型的地方应尽量避免使用 `any`。 TypeScript 的 `unknown` 类型表示一个未知的类型。它比 `any` 类型更加严格,因为在使用前需要进行类型检查。在某些情况下,你可能希望接收一个未知的值,但是又不想使用 `any` 类型,此时就可以使用 `unknown` 类型TypeScript 的 `never` 类型表示永远不会有返回值的函数的返回值类型。它也可以表示永远不会被执行的代码块的类型。例如,当某个函数的所有可能执行路径都会抛出异常时,它的返回值类型就可以是 `never`。 希望这对你有所帮助。 ### 回答2: TypeScript的`any`、`unknown`和`never`是三种特殊的类型。 1. `any`类型表示任何类型的值都可以赋值给它。它相当于取消了类型检查,可以在编写代码时快速推进开发,但也容易引入潜在的类型错误。使用`any`类型时需要慎重,最好在必要情况下使用,例如在需要与非TypeScript代码进行交互或在临时情况下。 2. `unknown`类型TypeScript 3.0引入的一种类型。与`any`相比,`unknown`类型提供了更加安全的动态类型。当我们不确定变量的类型时,可以使用`unknown`类型来保持类型的不确定性。在使用`unknown`类型的值时,我们必须首先进行类型检查或类型断言,以将其转换为更具体的类型。与`any`不同,`unknown`类型不会自动允许我们执行任意操作。这种类型的引入提高了代码的类型安全性,减少了潜在的类型错误。 3. `never`类型表示它永远不会发生的类型,即表示从不返回值的函数的返回类型以及永远无法完成的操作的结果类型。例如,在一个函数抛出了异常,或者存在无限循环。使用`never`类型可以更好地定义某些情况下的函数或操作的返回类型,同时帮助我们避免潜在的逻辑错误。 ### 回答3: TypeScript是一种静态类型的编程语言,它可以在JavaScript的基础上添加静态类型检查和一些其他特性。在TypeScript有三种特殊的类型:any、unknown和never。 any类型表示任意类型,它可以用来表示任何类型的值。使用any类型可以绕开类型检查,编译器不会对any类型的值进行类型检查和推断。any类型的变量可以接受任何类型的值,因此在使用any类型时需要特别小心,因为它可能导致类型错误和运行时错误。 unknown类型也表示任意类型,但它比any类型更严格。当使用unknown类型时,编译器会要求进行类型检查和类型断言,以确保类型安全。unknown类型的变量不能直接赋值给其他类型的变量,必须经过类型断言或类型检查后才能进行赋值。这种类型更适合在编写通用代码或处理动态类型的情况下使用,相比any类型unknown类型提供了更加严格的类型安全。 never类型表示永远不会发生的类型。它通常会用在函数的返回类型,表示该函数永远不会正常返回。例如,一个会抛出异常或进入无限循环的函数的返回类型可以设为never。同时,never类型也可以用来处理不可到达的代码块,例如一个带有无限循环的函数的后续代码块。 总而言之,在TypeScript,any类型unknown类型都表示任意类型,但unknown类型更加严格,并且要求进行类型检查和类型断言。而never类型表示永远不会发生的类型,通常用于函数的返回类型或不可到达的代码块。使用这些类型要谨慎,并且根据实际需求来选择合适的类型

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值