背景
最近在某项目中尝试迁移npm
至pnpm
,提交代码时突然发现有一个文件中引用了一个未定义在dependence中的依赖包的类型定义。
import { AlignType } from 'rc-table/lib/interface';
const center: AlignType = 'center';
复制代码
首先考虑一下为什么迁移会导致这行代码报错?
在package.json
中未定义相关依赖时,这样直接引入肯定是有问题的,而在使用npm
没有报错的本质原因就在npm
和pnpm
对node_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
或者number
,ts
在做类型推断的时候,会将其直接定义为文本类型,而不是通用的string
。
那么在此处,我们如果删除AlignType
这个类型定义,而让ts
自己去做一个类型推断是否可行呢?
我们将代码修改如下
const center = 'center';
const columns = [
{
title: '名称',
dataIndex: 'name',
alien: center,
},
{
title: '年龄‘,
dataIndex: 'age',
}
];
return <Table rowKey='id' columns={colums} />
复制代码
如上代码运行会报错如下
从报错信息可以发现,
ts
认为在columns
中,alien
是一个string
类型。这就有点奇怪了,因为我们明明使用了ts
的类型推断,将其类型固定在'center'
这一个特定字符串了呀,为什么ts
在此处推断center
是一个string
类型呢?
ts中的const
通过上述分析,可以发现我们遇到了一个问题。当我们没有定义一个常量的类型时,ts
会如何去做类型推断。
查阅ts
的官方文档,有两小节专门描述了上述以上两种情况,即定义string、number
和object
类型的变量时,ts
会有不同的类型推断。我们展开说一下object
。
当我们使用对象来初始化一个变量时,ts
会认为这个对象的属性在之后是可以变更的,例如
const obj = { count: 0 }
if (true) {
obj.count = 1
}
复制代码
即此处,obj
的类型被推断为{ count: number }
。套用罗翔老师的话,其实这是符合我们朴素的认知的。
只是这样会导致一个问题,如果我们需要将obj
赋值给某个属性,也就是类似第一段贴出的代码中,将columns
赋值给Table
组件的columns
属性,由于columns
的alien
的类型定义为'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
,而不是通用的string
或number
。
参考链接
typescript文档:www.typescriptlang.org/docs/handbo…