如何在 class 中存储属性的元信息

How to store meta information of properties in a class

我想将元数据关联到 classes 中的属性,特别是 属性 名称的缩写。

使用注释:@shortName(abbreviated) 你可以标记每个 属性:

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"}

问题是我在哪里以及如何存储这些关系,以便在派生的 classes 实例中可以检索它们?

最初,我创建了一个前缀为 target.constructor.name 的全局地图(它给出了 class 的名称)。但是在继承的 class 中, constructor.name 是继承的(所以在例子中 client 我会忘记 typeOfStorage

(这个的用途是保存 space 存储对象在 nonSql DB -firestore- 存储每个对象记录的每个 属性 名称)

我可能会在 class 的原型上存储一张地图。当解析对象的属性时,您可以通过递归调用 Object.getPrototypeOf 来获取原型链,从对象实例开始。您将不得不合并原型链中的所有地图(或者只在每个地图中单独查找 属性)。

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;
}