TS中any、void 和 never的区别

分类:前端来源: 掘金 最近更新:2022-08-03 12:07:16浏览:1934留言:0

TS中any、void 和 never的区别

在 TS 的类型系统中,除去直观的一些 number, string, boolean 等类型外,我们也会见到诸如 any、void 和 never 这样,没有那么直观的类型表达。

我们先简单的理解一下这几个

  • any 无法确定变量的类型时(或者无需确认类型时)

  • void 类型表示没有任何类型

  • never 类型表示永远不会有值的一种类型

any 类型

在一些情况下,如果我们无法确定变量的类型时(或者无需确认类型时),我们可以将其指定为 any 类型。

function isNumber(value: any) {
    return typeof value === 'number';
}

实际上,TS中对于被标记为 any 类型的变量,是没有进行类型检查而直接通过编译阶段的检查。

We want to opt-out of type checking and let the values pass through compile-time checks. To do so, we label these with the any type


当然,我个人的观点是,在我们的系统中还是应当尽量避免使用 any 类型,以尽可能的保证系统健壮性。

any 类型的特点

被标记为any类型的变量,具有以下几个特点:

允许赋值为任意类型

let value: any = 'seven';value = 7;

可以访问任意属性和方法

let value: any = 'hello';

// 可以访问任意属性
console.log(value.name);
console.log(value.name.firstName);

// 可以调用任意方法
value.setName('Jerry');
value.setName('Jerry').sayHello();
value.name.setFirstName('Cat');

要注意的一点是,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。

所以当你一旦使用了一个 any 类型后,很可能意味着打开了潘多拉魔盒,会让某一块的代码变得难以维护。而就算使用了断言,也丧失了在静态类型检查阶段发现错误的可能性。

变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型

let value;
value = 'seven';
value = 7;

这里再简单提一个小点,未声明类型的变量虽然一开始被识别为 any 类型,但是经过赋值后,TS 会根据赋值类型来标识变量的类型

什么意思呢,看下面

let value;

value = 'seven';
const diff1 = value - 1;
// 类型检查错误: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type

value = 7;
cosnt diff2 = value - 1;

void 类型

void 类型表示没有任何类型

void is a little like the opposite of any: the absence of having any type at all

没有返回值的函数,其返回值类型为 void

function warnUser(): void {
    console.log("This is my warning message");
}


申明为 void 类型的变量,只能赋予 undefined 和 null

let unusable: void = undefined;

never

never 类型表示永远不会有值的一种类型。(很抽象是不是)

The never type represents the type of values that never occur.

举几个例子:

  • never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型

// 返回never的函数必须存在无法达到的终点

// 因为总是抛出异常,所以 error 将不会有返回值
function error(message: string): never {
    throw new Error(message);
}

// 因为存在死循环,所以 infiniteLoop 将不会有返回值
function infiniteLoop(): never {
    while (true) {
    }
}
  • 永远不可能存在的情况:

const foo = 123;
if (foo !== 123) {
    const bar = foo;    // bar: never
}

never 类型的一个应用场景

那 never 类型到底有什么用呢?

举一个我们可能会见到的例子:

// ../../type.ts
enum Letter {
    A,
    B,
    C,
}

// 假如我们有如下一个通用型的业务逻辑
// ../../business.ts
switch (letter) {
    case Letter.A:
        // ...
        break;
    case Letter.B:
        // ...
        break;
    case Letter.C:
        // ...
        break;
    default:
        /**
         * 这里的我们通常会有两种做法:
         *      1. 给一个默认的状态
         *      2. 将其等同于A/B/C的一种
         *
         * 但其实我们默认代码不应该存在进入这一分支的可能,一旦进入意味着程序存在了某种异常的状态。
         **/
         
        // ...
        break;
}

其实我们默认代码不应该存在进入 default 分支的可能,一旦进入意味着程序存在了某种异常的状态。

假如我们将来在某个时刻给枚举值 Letter 增加了新的类型 D,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG 。

而且这将是一个“隐蔽型”的BUG,如果回归面不够广,很难发现此类BUG。

那 TS 有没有办法帮助我们在类型检查阶段发现这个问题呢?

答案就在下面

switch (letter) {
    case Letter.A:
        // ...
        break;
    case Letter.B:
        // ...
        break;
    case Letter.C:
        // ...
        break;
    default:
        const check: never = customType;
         
        // ...
        break;
}

由于任何类型都不能赋值给 never 类型的变量,所以当存在进入 default 分支的可能性时,TS的类型检查会及时帮我们发现这个问题。

never 和 void 的差异

void 表示没有任何类型,never 表示永远不存在的值的类型。

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

// 这个函数不能申明其返回值类型是 never
function warnUser(): void {
    console.log("This is my warning message");
}


0

发表评论

评论列表(0)

  • 暂时没有留言
热门