如何理解typescript中的const

背景

最近在某项目中尝试迁移npmpnpm,提交代码时突然发现有一个文件中引用了一个未定义在dependence中的依赖包的类型定义。

import { AlignType } from 'rc-table/lib/interface';
const center: AlignType = 'center';
复制代码

首先考虑一下为什么迁移会导致这行代码报错?

package.json中未定义相关依赖时,这样直接引入肯定是有问题的,而在使用npm没有报错的本质原因就在npmpnpmnode_modules不同的管理方式。

思考到这里,不去细究代码的话,可能直接就操作pnpm i安装对应依赖就完事。 但是,项目里面真的需要依赖这个包吗?

全局搜索后发现,项目中仅这一处使用到了rc-table,而这一处仅仅是为了使用它内部的一个类型定义,那我们再来分析一下,这一处类型定义的导出是否是必要的?

代码分析

先放上来一个简易版的demo

import { AlignType } from 'rc-table/lib/interface';
const center: AlignType = 'center';
const columns = [
    {
        title: '名称',
        dataIndex: 'name',
        alien: center,
    },
    {
        title: '年龄‘,
        dataIndex: 'age',
    }
];

return <Table rowKey='id' columns={colums} />
复制代码

我们发现,AlienType的主要作用是定义了常量center的类型,但是我们知道,如果我们使用const定义一个常量其值类型为string或者numberts在做类型推断的时候,会将其直接定义为文本类型,而不是通用的string

那么在此处,我们如果删除AlignType这个类型定义,而让ts自己去做一个类型推断是否可行呢?

我们将代码修改如下

const center = 'center';
const columns = [
    {
        title: '名称',
        dataIndex: 'name',
        alien: center,
    },
    {
        title: '年龄‘,
        dataIndex: 'age',
    }
];

return <Table rowKey='id' columns={colums} />
复制代码

如上代码运行会报错如下

截屏2022-06-15 下午2.10.23.png 从报错信息可以发现,ts认为在columns中,alien是一个string类型。这就有点奇怪了,因为我们明明使用了ts的类型推断,将其类型固定在'center'这一个特定字符串了呀,为什么ts在此处推断center是一个string类型呢?

ts中的const

通过上述分析,可以发现我们遇到了一个问题。当我们没有定义一个常量的类型时,ts会如何去做类型推断。

查阅ts的官方文档,有两小节专门描述了上述以上两种情况,即定义string、numberobject类型的变量时,ts会有不同的类型推断。我们展开说一下object

当我们使用对象来初始化一个变量时,ts会认为这个对象的属性在之后是可以变更的,例如

const obj = { count: 0 }
if (true) {
    obj.count = 1
}
复制代码

即此处,obj的类型被推断为{ count: number }。套用罗翔老师的话,其实这是符合我们朴素的认知的。

只是这样会导致一个问题,如果我们需要将obj赋值给某个属性,也就是类似第一段贴出的代码中,将columns赋值给Table组件的columns属性,由于columnsalien的类型定义为'center' | 'left'...,但是ts认为columns变量中的alien可能会有别的情况(也就是推断其类型为string而非center),导致类型不匹配。

好了我们现在知道了主要的原因,ts文档基于这种情况也给出了两种解决方式

1、可以在任一位置添加类型断言来更改类型推断

// change 1:
const columns = [
    {
        title: '名称',
        dataIndex: 'name',
        alien: 'center' as 'center',
     }
]
// change 2:
const columns = [
    {
        title: '名称',
        dataIndex: 'name',
        alien: 'center' as const,
     }
]
复制代码

2、你可以使用as const将对象转换为type literals(没有找到合适的翻译,暂且理解为文本类型)

const obj = { count: 0 } as const
复制代码

注意:如果使用as const进行类型转换,会将对象的类型都变成readonly,即obj的类型为{ readonly count: 0 }。所以第二种方式其实不适用于上文中我们遇到的问题。官方的例子是获取到对象里单个属性并作为参数传递,第二种方式是适用的。

后记

我们提到了as const去做类型转化,这是ts在3.7版本提出的新功能,as const的作用就类似于const,但是是作用于类型系统的,确保为所有属性分配的是type literals,而不是通用的stringnumber

参考链接

typescript文档:www.typescriptlang.org/docs/handbo…

猜你喜欢

转载自juejin.im/post/7109362597920505863