写了好几年的前端代码,但是面向对象相关的东西基本上很少用到。
其实我一直觉得js的面向对象是残疾的,对比java,C#之类的语言。。。 而且还还有很多坑,比如this的问题。基本上现在我就几乎不会用到this了,所以也就碰不到this的问题。。
有了typescript以后,算是好用一些,能帮你避免一些问题。也支持private修饰符等等很多原生js没有的功能。
最近我在封装枚举类的时候碰到了问题,这个需求比较常见,后端经常定义一些各种状态的枚举,然后数据库里面都是存的数字的字符串。因此返回前端的数据也是各种数字,所以前端需要定义这些状态,一开始我都是直接用一个字典来存的。创建了两种字典,一种是键用数字,一种是值用数字。。。定义完一种,另一种调用lodash的方法 _.mapKey就得到了。
我在网上刷到过相关的文章,有的人封装了工具函数,用来获得各种各样的枚举满足不同的需求,有的人用类来封装继承了Array。都是用typescript添加了类型提示。。。
我发现继承Array来实现不错,因为这样就能继承array的方法了。可以当作正常的数组来用,而且通过对象的方法来获取对应的值,IDE的提示更友好,用起来也舒服。
参考网上的代码继承数组,结果使用的时候遇到了报错//TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function
export interface EnumArrayObj {
value: number | string
label: string
}
export class EnumArray<
T extends readonly EnumArrayObj[],
> extends Array<EnumArrayObj> {
constructor(list: T) {
super(...list)
}
}
export const sexEnum = new EnumArray([
{
label: '男',
value: '1',
},
{
label: '女',
value: '2',
},
] as const),
console.log(
'test enum filter',
sexEnum.filter((item) => {
return item.label === '女'
}),
) //TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function
在网上搜这个报错基本上搜不到什么东西。。。
问chatGPT和new bing,也不太行。
最后我自己解决了。
既然报错的是...扩展运算符,那么不用这个就行了。。。
一开始我想到了Array.from,但是出现了其他报错,并没有解决问题。
后面又想到是不是没有实现迭代器的接口导致的。。。
最后还是解决了。因为我想到super其实就是Array的构造器,但是ts类型提示的是,参数是(length:number)
也就是创建一个长度为length的数组,并且初始值都是undefined
所以,直接用for循环给这个数组赋值就可以了。这样就不会用到扩展运算符。。。
最终我实现的代码如下
// 枚举类型接口
export interface EnumArrayObj {
value: number | string
label: string //中文key,方便阅读
displayText?: string //展示的文字,只有和label不同的时候使用,
}
export type ValueOf<T extends readonly EnumArrayObj[]> = T[number]['value']
export type LabelOf<T extends readonly EnumArrayObj[]> = T[number]['label']
export type ItemOf<T extends readonly EnumArrayObj[]> = {
value: ValueOf<T>
label: LabelOf<T>
displayText?: string
}
/**
* 枚举数组类,继承了Array
*/
export class EnumArray<
T extends readonly EnumArrayObj[],
> extends Array<EnumArrayObj> {
private readonly kvMap = new Map<string, ValueOf<T>>()
private readonly vkMap = new Map<string, LabelOf<T>>()
constructor(list: T) {
super(list.length)
for (let i = 0; i < list.length; i++) {
const item = list[i]
this[i] = item
this.kvMap.set(item.label, item.value)
this.vkMap.set(item.value + '', item.label)
}
}
getLabelByValue(value: ValueOf<T>) {
return this.vkMap.get(value + '')
}
getValueByLabel(label: LabelOf<T>) {
return this.kvMap.get(label)
}
getItemByLabel(label: LabelOf<T>): ItemOf<T> | undefined {
return this.find((item) => {
return item.label === label
})
}
getItemByValue(value: ValueOf<T>): ItemOf<T> | undefined {
return this.find((item) => {
return item.value === value
})
}
getDisplayTextByLabel(label: LabelOf<T>) {
const item = this.getItemByLabel(label)
return item?.displayText ?? label
}
getDisplayTextByValue(value: ValueOf<T>) {
const item = this.getItemByValue(value)
return item?.displayText ?? item?.label
}
}
export function createEnum<T extends readonly EnumArrayObj[]>(enums: T) {
return Object.freeze(new EnumArray(enums))
}
使用的时候,比较方便,因为label的类型,用于状态判断还是挺常用了,所以也获取类型导出。。。
const sexList = [
{
label: '男',
value: 1,
},
{
label: '女',
value: 2,
},
] as const
export const sexEnum = createEnum(sexList)
export type sexLabel = LabelOf<typeof sexList>
网友评论