TransWikia.com

Typescript перебор свойств

Stack Overflow на русском Asked by hlearn on December 9, 2021

Есть иммутабельный объект с большим количеством свойств (в т.ч. вычисляемых):

class Test {
private _a: string;
private _b: string;
private _c: string;

constructor(a: string = '', b: string = '', c: string  = '') {
    this._a = a;
    this._b = b;
    this._c = c;
}

public get a(): string {
    return this._a;
}

public get b(): string {
    return this._b;
}

public get c(): string {
    return this._c;
}

public get abc(): string {
    return this._a + this._b + this._c;
}
}

В него нужно добавить функцию слияния объектов:

public static merge(...objs: Test[]): Test {
    let t: Test = new Test();
    let key: keyof Test;
    for (let o of objs) {
        for (key in o) {
            if (o[key]) {
                t[key] = o[key]; // (*)
            }
        }
    }
    return t;
}

Однако при копировании свойства (*) возникает ошибка: "Cannot assign to ‘a’ because it is a read-only property", т.к. keyof предоставляет открытые свойства, в т.ч. геттеры. Как можно скопировать private свойства без ручного перебора?

t._a = o._a;
t._b = o._b;
t._c = o._c;

3 Answers

Нашлось простое, но не лучшее решение - теперь есть риск опечатки, т.к. будет принято любое значение ключа. Но задача решена:

class Test {
    private data: {[key: string]: string} = {};
    
    constructor(a: string = '', b: string = '', c: string  = '') {
        this.data.a = a;
        this.data.b = b;
        this.data.c = c;
    }
   
    public static merge(...objs: Test[]): Test {
        let t: Test = new Test();
        for (let o of objs) {
            for (let key in o.data) {
                if (o.data[key]) {
                    t.data[key] = o.data[key];
                }
            }
        }
        return t;
    }
}

Answered by hlearn on December 9, 2021

class Test {

    constructor(
        private _a: string,
        private _b: string,
        private _c: string,
    ) { }

    get a(): string {
        return this._a;
    }

    get b(): string {
        return this._b;
    }

    get c(): string {
        return this._c;
    }

    get abc(): string {
        return this._a + this._b + this._c;
    }

    merge(...objList: any[]): this {
        let key: any;
        for (let obj of objList) {
            for (key in obj) {
                if (isKeyof(this, key)) {
                    /**
                     * k = `${key}` or `_${key}`
                     */
                    let k = key;
                    // #region FIXME:
                    /** key is getter? */
                    if (isGetter(this, key)) {
                        // @ts-ignore
                        k = `_${key}`
                        if (!(k in this)) {
                            throw new TypeError('E_R_R')
                        }
                    }
                    // #endregion
                    this[k] = obj[key]; // (*)
                }
            }
        }
        return this;
    }
}

function isGetter(obj: any, prop: string | number | symbol) {
    const desc = Object.getOwnPropertyDescriptor(obj, prop)
    return undefined !== desc && 'get' in desc
}

function isKeyof<T>(obj: T, k: any): k is (keyof T) {
    return k in obj
}

Answered by qwabra on December 9, 2021

Тогда перепишите constructor немного

class Test {
 private _a: string;
 private _b: string;
 private _c: string;

 constructor(builder: Test) {
    this._a = builder.a;
    this._b = builder.b;
    this._c = builder.c;
 }

 public get a(): string {
    return this._a;
 }

 public get b(): string {
    return this._b;
 }

 public get c(): string {
    return this._c;
 }

 public get abc(): string {
    return this._a + this._b + this._c;
 }
}

Answered by Aziz Umarov on December 9, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP