·  阅读 15638

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

那么希望通过这篇文章,希望能帮助大家来简单了解一下 TS 类型中的 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 {
    // 假如我们有如下一个通用型的业务逻辑
    // ../../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的类型检查会及时帮我们发现这个问题。参考

    再补充一点个人观点,在这个 default 分支中,除了使用 never 来帮我们尽可能避免这一问题外,我们依旧需要做好降级处理,因为依然存在可能由于异常进入 default 分支,我们需要采取默认值或者其他合适的方法来保证系统的稳定。

    never 和 void 的差异

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

    // 返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
        while (true) {
    // 这个函数不能申明其返回值类型是 never
    function warnUser(): void {
        console.log("This is my warning message");
    复制代码

    关于本文的内容,参考了很多这一文档,有兴趣的同学可以去看看。也非常感谢作者付出!

    分类:
    前端
    标签: