How to store meta information of properties in a class

末鹿安然 提交于 2021-01-29 11:17:21

问题


I want to associate metadata to properties in classes, specifically abbreviations of property names.

With annotations: @shortName(abbreviated) you can tag each property:

function shortName(shortName: string){
    return function (target: Object, realName: string){
        // Where to store the relation realName <-> shortName ??
    }
}
class Record{
    @shortName("ts") typeOfStorage: string;
}
class Client extends Record{
    @shortName("df") descriptiveField: string;
}
function mapNames(obj: any){  // Return object with shortened names
    let ret = {};
    for(let prop in obj){
        //Here retrieve the short name and add to ret
    }
    return ret;
}

let client = new Client();               // supposing: { typeOfStorage: "f", descriptiveField: "blah"}
let clientShortened = mapNames(client);  // expected:  {ts: "f", df: "blah"}

The question is where and how do I store these relations so they are retrievable in instances of derived classes?

Initially, I was creating a global map prefixing with target.constructor.name (that gives the name of the class). But in an inherited class the constructor.name is the inherited one (so in the example client I'd lose track of typeOfStorage

(The use of this is to save space storing objects in nonSql DB -firestore- that store each property name of each object-record)


回答1:


I would probably store a map on the prototype of the class. When resolving properties for an object you then can acquire the prototype chain by recursively calling Object.getPrototypeOf, starting with the object instance. You will have to merge all the maps in the prototype chain (or just look up the property in every map individually).

function shortName(shortName: string)
{
    return function (target: Object, realName: string)
    {
        const t = target as { _propMap?: Map<string, string> };

        if (t._propMap == null)
        {
            // This is probably overkill, because people usually iterate
            // properties on the instances, not the prototype.
            // Setting enumerable: false hides the property.
            Object.defineProperty(t, '_propMap', {
                enumerable: false,
                writable: true,
            });

            t._propMap = new Map<string, string>();
        }

        t._propMap.set(realName, shortName);
    }
}

function getMap(obj: any)
{
    // Might want to get the chain first, then iterate in reverse
    // so child properties override parent properties
    const map = new Map<string, string>();
    while (true)
    {
        obj = Object.getPrototypeOf(obj);
        if (obj == Object.prototype)
            return map;

        if (obj._propMap)
        {
            let subMap = obj._propMap as Map<string, string>;
            subMap.forEach((v, k) => map.set(k, v));
        }
    }
}

function mapNames(obj: any)
{
    const map = getMap(obj);

    const ret: any = {};
    for (let prop in obj)
    {
        const name = map.get(prop) ?? prop;
        ret[name] = obj[prop];
    }

    return ret;
}


来源:https://stackoverflow.com/questions/64388534/how-to-store-meta-information-of-properties-in-a-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!