简体   繁体   English

Babel-装饰类属性的装饰器在实例化类之前被调用

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

Excuse me for creating a new question, I was not able to find a question addressing this matter. 对不起,我提出了一个新问题,但我找不到解决此问题的问题。

I am having difficulties testing my dependency injection using mocha and experimental es6+ decorators transpiled using babel. 我在使用mocha和使用babel转译的实验性es6 +装饰器测试我的依赖项注入时遇到困难。 The class property decorator is being called before it should've been. 应该在调用类属性装饰器之前就调用它。

injection.test.js (mocha test, using --require babel-register ) 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'));
        })
    });
});

When running the test, the decorated class is transformed as inteded. 运行测试时,修饰后的类将按inted进行转换。 However, the sampleService property of the instance of Dependant , created in the second test, is undefined. 但是,在第二个测试中创建的Dependant实例的sampleService属性未定义。

The decorator in question should be called/invoked once an instance of the class is being created, but the decorator is called when the class is defined and the property is decorated. 一旦创建了类的实例,就应该调用/调用有问题的装饰器,但是当定义了类并装饰了属性时,将调用装饰器。 The expected behaviour is maintained when using TypeScript . 使用TypeScript时,可以维持预期的行为。

Below I've listed the (simplified) decorators and my babel configuration. 在下面,我列出了(简化的)装饰器和babel配置。

.babelrc .babelrc

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

exported decorator Inject (targeting class property ): 导出的装饰器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);
        }
    };
};

exported decorator Injectable (targeting class ): 导出的装饰器Injectable(定位class ):

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

I still haven't found the leading cause why it creates a new instance of the class when decorating the class property . 我还没有找到装饰类属性时为什么会创建类的新实例的主要原因。 However, I have found a working solution to my problem. 但是,我已经找到了解决我的问题的可行方法。 Within mocha, using --require babel-register and the legacy decorator plugin, the class property decorator makes use of the PropertyDescriptor . 在mocha中,使用--require babel-register和旧版装饰器插件,类属性装饰器使用PropertyDescriptor Changing the simplified Inject decorator to the following has solved my problem of not instantiating my decorated class property. 将简化的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;
        }
    };
};

Deleting the writable property is neccesary. 删除writable属性是必要的。

The following tests... 以下测试...

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

...output the following result: ...输出以下结果:

  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.

相关问题 实例化导出类的属性时出错 - Error instantiating properties of an exported class 带有第 2 阶段提案和 Babel 7 的类装饰器 - Class decorator with stage-2 proposal and Babel 7 NestJs:确保您的 class 使用适当的装饰器进行装饰 - NestJs: Make sure your class is decorated with an appropriate decorator Javascript用父属性实例化子类 - Javascript instantiating child class with parent properties 获取类的属性数组而不实例化它? - Get array of class's properties without instantiating it? 使用Babel将Arrow用作类属性 - Arrow functions as class properties using Babel 带有装饰器和 class 属性的 Babel 配置不起作用 - Babel configuration with decorators & class properties not working typescript类装饰器:在装饰器函数中定义的类型属性 - typescript class decorator: typing properties defined in decorator function 如何创建一个装饰器来生成一个新的 class 属性,该属性是装饰属性的映射或转换 - How to create a decorator that produces a new class property that is a mapping or transformation of the decorated property NestJs - Graphql 错误无法确定 GraphQL 输入类型<field> . 确保您的 class 已使用合适的装饰器进行装饰</field> - NestJs - Graphql Error Cannot determine a GraphQL input type for the <Field>. Make sure your class is decorated with an appropriate decorator
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM