TypeScript 扩展的类型 never、void、字符串、数字、布尔值字面量

634 阅读4分钟

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

新增的扩展类型

TS 作为 JS 的超集,除了支持 JS 的类型之外,还提供了一些额外的扩展类型,比如说,元组,枚举等,这些扩展类型的加入丰富了 TS的数据类型,利用好这些扩展类型,我们就可以提高代码的易读性,以及健壮性。

听起来是不是很酷,下面我们就来学习一下 TS 的扩展类型吧

资料-特殊的函数返回值

never

为 TypeScript 增加 never 类型: github.com/Microsoft/T…

在上述的链接中,作者为 TypeScript 新增了 never 类型。其有两种具体场景:

  • 作为不会返回( return )的函数的返回值类型
  • never 是类型保护机制下,永远不会发生改变的变量类型

作为不会返回( return )的函数的返回值类型

以下是作者给出的示例代码,在以下场景中,函数的返回值是 never。

// 返回never的函数 必须存在 无法达到( unreachable ) 的终点
function error(message: string): never {
    throw new Error(message);
}

// 由类型推论得到返回值为 never
function fail() {
    return error("Something failed");
}

// 返回never的函数 必须存在 无法达到( unreachable ) 的终点
function infiniteLoop(): never {
    while (true) {
    }
}

never 是类型保护机制下,永远不会被观察到( observed )的变量类型

在 TS 中, null 和 undefined 是任何类型的有效值,所以无法正确地检测它们是否被错误地使用。于是 TS 引入了 --strictNullChecks 这一种检查模式。 由于引入了 --strictNullChecks ,在这一模式下,null 和 undefined 能被检测到。所以 TS 需要一种新的底部类型( bottom type )。所以就引入了 never。

「never 是类型保护机制下,永远不会发生改变的变量类型」,这一说法看起来比较绕,因为这不符合我们日常的编程习惯,还是用代码来解释,我们看以下代码:

// Compiled with --strictNullChecks
function fn(x: number | string) {
  if (typeof x === 'number') {
    // x: number 类型
  } else if (typeof x === 'string') {
    // x: string 类型
  } else {
    // x: never 类型
    // --strictNullChecks 模式下,这里的代码将不会被执行,x 无法被观察
  }
}

void

void 表示没有任何类型。 当一个函数没有返回值时,TS 会认为它的返回值是 void 类型。例如:

function foo(): void {
    alert('foo');
}

当我们声明一个变量类型是 void 的时候,它仅可以被赋值为 null 和 undefined;

let unusable: void = undefined;

never 和 void 的区别

  • void 可以被赋值为 null 和 undefined的类型。 never 则是一个不包含值的类型。
  • 拥有 void 返回值类型的函数能正常运行。拥有 never 返回值类型的函数无法正常返回,无法终止,或会抛出异常。

字符串、数字、布尔值字面量

接下来,我们学习字面量,字面量包含字符串、数字、布尔值字面量。

我们先来看看之前见过的报错信息

let seven: number = 7;

seven = 'Seven'; // 报错,不能将类型“Seven”分配给类型“number”
// 为什么这里写的是类型“Seven”呢?这个东西,不应该是 string 类型吗?其实这个 “Seven” 就是字符串字面量类型。

字符串字面量

一般我们会使用联合类型定义多个字符串字面量。

type FavoriteNumber = 'One' | 'Two' | 'Seven';

let seven: FavoriteNumber = 'Seven';
// 如果是上面三者不存在的,就会报错,不能将类型“Six”分配给类型“FavoriteNumber”
// 同时,我们在使用的时候,也会有代码提示我们只能选择这三者中某一者。
let seven: FavoriteNumber = 'Six';

数字字面量,跟字符串字面量类似,可以直接使用数字来约束,number 必须是 1,2,7中的某一个,如果不是的话,肯定会报错。

type FavoriteNumber = 1 | 2 | 7;

let seven: FavoriteNumber = 7;

同时数字字面量和字符串字面量,是可以使用联合类型混用的。

这样的话,seven 要么是这个字符串,要么是这个数字。

type FavoriteNumber = 1 | 'Seven';

let seven: FavoriteNumber = 'Seven';

布尔值字面量

type FavoriteNumber = 1 | 'Seven' | true;

let seven: FavoriteNumber = false;

字符串字面量 vs 联合类型

我们先来看一下二者的定义

字符串字面量

字符串字面量类型用来约束取值只能是某几个字符串中的一个。

type EventNames = 'click' | 'scroll' | 'mousemove';

function handleEvent(ele: Element, event: EventNames) {};

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题

handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'

// error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.

联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

myFavoriteNumber = true;

// error TS2322: Type 'boolean' is not assignable to type 'string | number'.

二者的区别

理解二者的定义后,我们就能比较直观地看出二者的区别了。

  • 字符串字面量 限定了使用该字面量的地方仅接受特定的值
  • 联合类型 对于值并没有限定,仅仅限定值的类型需要保持一致