TypeScript小状况之用数组元素组装数据类型

423 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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,竟然通过了语法检查没有报错!!!

007qPM5Ngy1glowr3xhnnj306o06ojrk.jpeg

终章

细看RANKS的类型定义,很容易发现了端倪,不止有数字5,还有一大堆数组对象的方法,而这个数字5,正正就是数组的length的值。

Screen Shot 2022-10-04 at 5.20.55 PM.png

那么,问题应该就是出在K in keyof typeof RANK_LIST,遍历了RANK_LIST类型的所有的字段。

而实际上,我们只需要数字类型的字段,再经过简化,得出以下最终方案。

const RANK_LIST = ['Excellent', 'Outstanding', 'Good', 'Average', 'Poor'] as const

type RANKS = typeof RANK_LIST[number]

Screen Shot 2022-10-04 at 5.33.16 PM.png

总结

为了解决这个问题,学到了两个知识点:

  • 给数组设置as const使之定义为readonly
  • 使用keyof的时候,要注意到是会遍历到对象自带的属性和方法,要实际视乎情况决定是否保留