繁体   English   中英

是否可以为 class 创建动态属性?

[英]is it possible to create dynamic properties to a class?

我将枚举包装在 class 中,因此我可以 map 其值在 TypeScript

enum Color {
    RED = 0,
    GREEN = 1,
    BLUE = 2
}

const MapColor = new EnumMap(Color); // Wrapper

console.log(MapColor.map()) // [{ "key": "RED", "value": 0}, { "key": "GREEN","value": 1}, { "key": "BLUE",  "value": 2}] 
console.log(MapColor.entries()) // [["RED", 0], ["GREEN", 1], ["BLUE", 2]] 
console.log(MapColor.keys()) // ["RED", "GREEN", "BLUE"] 
console.log(MapColor.values()) //  [0, 1, 2] 

包装 Class

class EnumMap {
  private _map = new Map<string, string | number>();
  public enum: any;

  constructor(enumeration: any) {
    this.init(enumeration);
    return this;
  }

  private init(enumeration: any) {
    this._map = new Map<string, string | number>();

    for (let key in enumeration) {
      if (!isNaN(Number(key))) continue;
      const val = enumeration[key] as string | number;
      if (val !== undefined && val !== null) this._map.set(key, val);
    }

    this.enum = enumeration;
  }

  map = (): Object => Array.from(this._map.entries()).map((m) => ({ key: m[0], value: m[1] }));
  entries = (): any[] => Array.from(this._map.entries());
  keys = (): any[] => Array.from(this._map.keys());
  values = (): any[] => Array.from(this._map.values());
}

我想直接访问一个枚举值,我想这样做

MapColor.RED  // 0
MapColor.RED.toString()  // 'RED'

是否可以为 class 创建动态属性? 如果可能的话,它会怎么做?

在旁边:

我忽略了MapColor.RED0MapColor.RED.toString()评估为"RED"的请求。 如果您希望MapColor.RED === Color.REDtrue ,那么MapColor.RED是值为0number (原始数据类型),您完全不可能将"RED"作为其字符串值,因为(0).toString()只是"0" 如果您希望MapColor.RED成为一个Number (包装器对象),您可以将其作为Object.assign(Number(0), {toString(){return "RED"})来实现,但结果很糟糕 它有点像0 ( MapColor.RED + 5 === 5 ),除非它不是,因为MapColor.RED === 0false ,并且someObject[MapColor.RED]将访问someObjectRED属性,而不是0属性。 因此,您要么不能这样做,要么可以但不应该这样做。 所以我假装它不存在。


回答:

您可以通过将enumeration的属性直接复制到构造函数中的this实现这样的 class(我猜是init()方法)。 您可以通过交集使用generics描述此类 class 构造函数的类型。 你不能做的是让编译器验证实现是否符合描述。 因此,您需要在多个地方使用类型断言来告诉编译器您的 class 实例和 class 构造函数符合相关行为:

class _EnumMap<T extends Record<keyof T, string | number>> {
    private _map = new Map<keyof T, T[keyof T]>();
    public enum!: T;

    constructor(enumeration: T) {
        this.init(enumeration);
        return this;
    }

    private init(enumeration: T) {
        this._map = new Map<keyof T, T[keyof T]>();

        for (let key in enumeration) {
            if (!isNaN(Number(key))) continue;
            const val = enumeration[key];
            if (val !== undefined && val !== null) {
                this._map.set(key, val);
                (this as any)[key] = val; // set value here
            }
        }

        this.enum = enumeration;
    }

    map = () => Array.from(this._map.entries())
        .map((m) => ({ key: m[0], value: m[1] })) as
        Array<{ [K in keyof T]: { key: K, value: T[K] } }[keyof T]>;
    entries = () => Array.from(this._map.entries()) as
        Array<{ [K in keyof T]: [K, T[K]] }[keyof T]>;
    keys = () => Array.from(this._map.keys()) as Array<keyof T>;
    values = () => Array.from(this._map.values()) as Array<T[keyof T]>;
}

我已将EnumMap重命名为_EnumMap并在最后恢复EnumMap名称:

type EnumMap<T extends Record<keyof T, string | number>> = _EnumMap<T> & T;
const EnumMap = _EnumMap as 
  new <T extends Record<keyof T, string | number>>(enumeration: T) => EnumMap<T>;

重命名的EnumMap在传递给它的enumeration object 的类型T中是通用的。 init()我写this[key] = val ,但编译器不知道this应该有与 key 对应的key ,所以我将其断言this as any以放松编译器警告。 我也更改了所有现有方法的 output 类型,以更正确地对应于您从中得到的内容...但您没有要求,所以保留anyObject (或者可能是object ) .

通过编写type EnumMap<T> = _EnumMap<T> & T ,我是说EnumMap<T>既充当您的原始 class ,充当T 因此,如果T看起来有点像{RED: 0, GREEN: 1, BLUE: 2} ,那么EnumMap<T>也将具有这些属性。 通过将const EnumMap = _EnumMap as new<T>(enumeration: T) => EnumMap<T> ,我是说原来的 class 构造函数已被复制到一个名为EnumMap的值,并且可以被视为 class 构造函数对于新的EnumMap<T>的实例。


让我们测试一下:

enum Color {
    RED = 0,
    GREEN = 1,
    BLUE = 2
}

const MapColor = new EnumMap(Color); // Wrapper

console.log(MapColor.map()) // [{ "key": "RED", "value": 0}, 
  // { "key": "GREEN","value": 1}, { "key": "BLUE",  "value": 2}] 
console.log(MapColor.entries()) // [["RED", 0], ["GREEN", 1], ["BLUE", 2]] 
console.log(MapColor.keys()) // ["RED", "GREEN", "BLUE"] 
console.log(MapColor.values()) //  [0, 1, 2] 
console.log(MapColor.RED) // 0
console.log(MapColor.GREEN) // 1
console.log(MapColor.BLUE) // 2

在我看来很好。


Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM