Вложенный JS декоратор получить / установить, как правильно их цепочки? - PullRequest
0 голосов
/ 30 апреля 2020

Фреймворк ember активно использует декораторы. Теперь, чтобы использовать привязку данных, я должен украсить свои свойства @tracked, что дает мне все мои приятные обновления пользовательского интерфейса каждый раз, когда я меняю свойство.

     @tracked username = 'dave';

Это хорошо работает, но я сталкиваюсь с некоторыми серьезными проблемами, если мне нужно добавить собственный декоратор поверх отслеживаемого декоратора.

    @typed(StateTrackMap)
    @tracked
    mapConfigsArray = [create(StateTrackMap)];

Я могу заставить это работать, с помощью проверки моего @typed декоратора, чтобы увидеть, находится ли он выше другого декоратора или нет.

export default function typed(classType) {
    let weak = new WeakMap();
    return function(object, property, descriptor) {
        return {
            get() {
                // Check if there is another decorator attached below us in the chain
                // i.e. "tracked"
                if (typeof descriptor.get == 'function') {
                    return descriptor.get.call(this);
                }
                // If we haven't initialized before but there is one ready, return that
                if (!weak.has(this) && typeof descriptor.initializer == 'function') {
                    weak.set(this, descriptor.initializer.call(this));
                }
                return weak.get(this);
            },
            set(value) {
                // my set code which does the type checking/converting this descriptor is for

                                // Apply the converted value to the lower level object
                // This may be the object itself, or it may be another setter in the chain
                if (typeof descriptor.set == 'function') {
                    descriptor.set.call(this, typedValue);
                } else {
                    return weak.set(this, typedValue);
                }
            }
        }
    }
}

Но это кажется странным ... и не похоже ни на одно из использованных мной дескрипторов. Главным образом потому, что если я изменю порядок декораторов, то взорвется

    @tracked
    @typed(StateTrackMap)
    mapConfigsArray = [create(StateTrackMap)];
index.js:172 Uncaught Error: Assertion Failed: The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both.

Так что я думаю, мой вопрос в том, как правильно связать декораторы с get & set? Мне кажется, что порядок декораторов определяет, могу ли я go вверх / вниз по цепочке или нет. Также мне кажется, что логика цепочки c должна быть запечена в каждом декораторе, иначе она не работает. Есть ли какой-нибудь общий способ, которым я могу передать декораторы другим декораторам?

Я видел несколько примеров, в которых я возвращал ссылку на дескриптор, но это, похоже, не помогло решить проблему и здесь, так как я не совсем уверен, как я все еще могу вставить свой get/set в него, не стирая цепочка собственности или попадание в ту же лодку, что и выше, где мой код должен быть разработан специально для работы с другими дескрипторами.

export default function typed(classType) {
    return function(object, property, descriptor) {
        const set = descriptor.set;
        const get = descriptor.get;
        const weak = new WeakMap();

        descriptor.get = function() {
            if (typeof get == 'function') {
                return get.call(this);
            }
            // If we haven't initialized before but there is one ready, return that
            if (!weak.has(this) && typeof descriptor.initializer == 'function') {
                weak.set(this, descriptor.initializer.call(this));
            }
            return weak.get(this);
        }
        descriptor.set = function(value) {
            // My type checking / conversion code

            // Apply the converted value to the lower level object
            // This may be the object itself, or it may be another setter in the chain
            if (typeof set == 'function') {
                set.call(this, typedValue);
            } else {
                return weak.set(this, typedValue);
            }
        }
        return descriptor;
    }
}

Кстати, этот метод вызывает другой взрыв.

Assertion Failed: You attempted to use @tracked on mapConfigsArray, but that element is not a class field.
...