typescript枚举类的封装,继承Array时报错Sprea

2023-06-09  本文已影响0人  mudssky

写了好几年的前端代码,但是面向对象相关的东西基本上很少用到。

其实我一直觉得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>
上一篇 下一篇

猜你喜欢

热点阅读