持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
在项目内,使用TypeScript的过程中遇到了一些状况、报错或棘手的情况,决定开一个系列记录遇到的问题和我的解决方法。
背景
在某些业务场景里面,我们需要把一个列表中的元素的集合,定义为一种类型,用于用户输入或者接口返回的数据类型。
例如,我需要一个评价等级,包含以下字符串
- Excellent,极好
- Outstanding,优秀
- Good,良好
- Average,中等
- Poor,差
解决方案
初试
首先,按照要求,要有一个字符串数组。
然后,定义一个类型,需要获取字符串的元素的集合。
const RANK_LIST = ['Excellent', 'Outstanding', 'Good', 'Average', 'Poor']
type RANKS = {
[K in keyof typeof RANK_LIST]: typeof RANK_LIST[K]
}[keyof typeof RANK_LIST]
然而很快就发现不对劲,其他字符串都能赋值该类型的变量。原因是因为数组是可以增减元素,所以typeof RANK_LIST[K]的类型是string,而没有限制为具体的字符串。
完善
那我们需要把RANK_LIST声明为readonly,不能修改数组内容,也不能重新赋值。
const RANK_LIST = ['Excellent', 'Outstanding', 'Good', 'Average', 'Poor'] as const
type RANKS = {
[K in keyof typeof RANK_LIST]: typeof RANK_LIST[K]
}[keyof typeof RANK_LIST]
哈哈,瞬间就修改好了,一切看起来都是那么的顺利。
直到有一天,声明类型为RANKS的变量,鬼使神差的被赋值了一个数字5,竟然通过了语法检查没有报错!!!
终章
细看RANKS的类型定义,很容易发现了端倪,不止有数字5,还有一大堆数组对象的方法,而这个数字5,正正就是数组的length的值。
那么,问题应该就是出在K in keyof typeof RANK_LIST,遍历了RANK_LIST类型的所有的字段。
而实际上,我们只需要数字类型的字段,再经过简化,得出以下最终方案。
const RANK_LIST = ['Excellent', 'Outstanding', 'Good', 'Average', 'Poor'] as const
type RANKS = typeof RANK_LIST[number]
总结
为了解决这个问题,学到了两个知识点:
- 给数组设置
as const使之定义为readonly - 使用
keyof的时候,要注意到是会遍历到对象自带的属性和方法,要实际视乎情况决定是否保留