[英]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.