簡體   English   中英

Babel-裝飾類屬性的裝飾器在實例化類之前被調用

[英]Babel - decorator of decorated class properties is called before instantiating class

對不起,我提出了一個新問題,但我找不到解決此問題的問題。

我在使用mocha和使用babel轉譯的實驗性es6 +裝飾器測試我的依賴項注入時遇到困難。 應該在調用類屬性裝飾器之前就調用它。

injection.test.js(mocha測試,使用--require babel-register

import * as DependencyInjection from '../build/decorators/DependencyInjection';

@DependencyInjection.Injectable(service => service.injected = true)
class SampleService {

    property = 'default';

    constructor(property, ...data) {
        this.property = property || this.property;
    }
}

class Dependant {

    /** @type {SampleService} */
    @DependencyInjection.Inject(SampleService)
    sampleService;
}

describe('dependency injection', () => {

    describe('is decoratored as injectable', () => {
        it('should be injectable', done => done(SampleService.injectable ? void 0 : new Error('Injectable is not set')));

        it('should contain a predicate', done => done(SampleService.predicate ? void 0 : new Error('Predicate is not set')));
    });

    describe('inject injectable', () => {
        it ('should inject the injectable provider', done => {
            const dependant = new Dependant();

            done(!!dependant.sampleService ? void 0 : new Error('Injectable provider was not injected'));
        })
    });
});

運行測試時,修飾后的類將按inted進行轉換。 但是,在第二個測試中創建的Dependant實例的sampleService屬性未定義。

一旦創建了類的實例,就應該調用/調用有問題的裝飾器,但是當定義了類並裝飾了屬性時,將調用裝飾器。 使用TypeScript時,可以維持預期的行為。

在下面,我列出了(簡化的)裝飾器和babel配置。

.babelrc

{
    "presets": [
        "env",
        "stage-0",
        "es2017"
    ],
    "plugins": [
        "syntax-decorators",
        "transform-decorators-legacy",
        ["transform-runtime", { 
            "polyfill": false,
            "regenerator": true
        }]
    ]
}

導出的裝飾器Inject(定位class property ):

exports.Inject = (typeFunction, ...data) => {
    return function (target, propertyName) {
        try {
            const injected = target[propertyName] = new typeFunction(data);
            if ('predicate' in typeFunction && typeof typeFunction.predicate === 'function') {
                typeFunction.predicate(injected);
            }
        }
        catch (err) {
            throw new Error(err.message || err);
        }
    };
};

導出的裝飾器Injectable(定位class ):

exports.Injectable = (predicate) => {
    return function (target) {
        const provider = target;
        provider.injectable = true;
        if (predicate && typeof predicate === 'function') {
            provider.predicate = predicate;
        }
    };
};

我還沒有找到裝飾類屬性時為什么會創建類的新實例的主要原因。 但是,我已經找到了解決我的問題的可行方法。 在mocha中,使用--require babel-register和舊版裝飾器插件,類屬性裝飾器使用PropertyDescriptor 將簡化的Inject裝飾器更改為以下選項可以解決我不實例化裝飾類屬性的問題。

exports.Inject = (typeFunction, ...data) => {
    return function (target, propertyName, descriptor) {
        let value = null;
        try {
            const injected = value = target[propertyName] = new typeFunction(...data);
            if ('predicate' in typeFunction && typeof typeFunction.predicate === 'function') {
                typeFunction.predicate(injected);
            }
        }
        catch (err) {
            throw new Error(err.message || err);
        }
        if (descriptor) {
            delete descriptor.initializer;
            delete descriptor.writable;
            descriptor.value = value;
        }
    };
};

刪除writable屬性是必要的。

以下測試...

const assert = require('assert');
const chai = require('chai');

import * as DependencyInjection from '../build/decorators/DependencyInjection';

@DependencyInjection.Injectable(service => service.injected = true)
class SampleService {

    property = 'default';

    constructor(property, ...data) {
        this.property = property || this.property;
    }
}

class Dependant {

    /** @type {SampleService} */
    @DependencyInjection.Inject(SampleService)
    sampleService;
}

class Dependant2 {

    /** @type {SampleService} */
    @DependencyInjection.Inject(SampleService, 'overloaded')
    sampleService;
}

describe('dependency injection', () => {

    describe('is decoratored as injectable', () => {
        it('should be injectable', done => done(SampleService.injectable ? void 0 : new Error('Injectable is not set')));

        it('should contain a predicate', done => done(SampleService.predicate ? void 0 : new Error('Predicate is not set')));
    });

    describe('inject at decorated class property', () => {
        it('should inject the injectable provider at the decorated property', () => {
            const dependant = new Dependant();

            chai.expect(dependant.sampleService).to.be.instanceof(SampleService, 'Injectable provider was not injected');

            chai.assert.isTrue(dependant.sampleService.injected, 'The predicate of the injectable service was not set');

            chai.expect(dependant.sampleService.property).to.equal('default', 'The initial value of \'property\' was not \'default\'');
        });

        it('should inject the injectable provider with overloaded constructor arguments at the decorated property', () => {
            const dependant = new Dependant2();

            chai.expect(dependant.sampleService).to.be.instanceOf(SampleService, 'Injectable provider was not injected');

            chai.assert.isTrue(dependant.sampleService.injected, 'The predicate of the injectable service was not set');

            chai.expect(dependant.sampleService.property).to.equal('overloaded', 'The value of \'property\' was not overloaded');
        });
    });

    describe('inject at manual target and property', () => {
        it('should inject the injectable provider at the targeting value', () => {
            const inject = DependencyInjection.Inject(SampleService);

            const target = {};

            let err = null;
            try {
                inject(target, 'service');
            } catch (e) {
                err = e;
            }

            chai.assert.isNull(err, 'Expected injection to pass');

            chai.expect(target.service).to.be.instanceOf(SampleService, 'Injectable provider was not injected');

            chai.assert.isTrue(target.service.injected, 'The predicate of the injectable service was not set');

            chai.expect(target.service.property).to.equal('default', 'The initial value of \'property\' was not \'default\'');
        });

        it('should inject the injectable provider with overloaded constructor arguments at the targeting value', () => {
            const inject = DependencyInjection.Inject(SampleService, 'overloaded');

            const target = {};

            let err = null;
            try {
                inject(target, 'service');
            } catch (e) {
                err = e;
            }

            chai.assert.isNull(err, 'Expected injection to pass');

            chai.expect(target.service).to.be.instanceOf(SampleService, 'Injectable provider was not injected');

            chai.assert.isTrue(target.service.injected, 'The predicate of the injectable service was not set');

            chai.expect(target.service.property).to.equal('overloaded', 'The value of \'property\' was not overloaded');
        });

        it('should not inject anything at the targeting value', () => {
            const inject = DependencyInjection.Inject();

            const target = {};

            let err = null;
            try {
                inject(target, 'service');
            } catch (e) {
                err = e;
            }

            chai.expect(err).to.be.instanceof(Error);

            chai.assert.notExists(target.service);
        });
    });
});

...輸出以下結果:

  dependency injection
    is decoratored as injectable
      √ should be injectable
      √ should contain a predicate
    inject at decorated class property
      √ should inject the injectable provider at the decorated property
      √ should inject the injectable provider with overloaded constructor arguments at the decorated property
    inject at manual target and property
      √ should inject the injectable provider at the targeting value
      √ should inject the injectable provider with overloaded constructor arguments at the targeting value
      √ should not inject anything at the targeting value


  7 passing (29ms)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM