reactant-di
Version:
A dependency injection lib for Reactant
83 lines (79 loc) • 2.18 kB
text/typescript
import { METADATA_KEY } from '../constants';
import { ServiceIdentifier } from '../interfaces';
/**
* ## Description
*
* You can get a decorator `@lazy(serviceIdentifier)` with `getLazyDecorator((serviceIdentifier) => container.get(serviceIdentifier))`,
* and use it on any one dependency property that you need to lazily get.
*
* ## Example
*
* ```ts
* let container: Container;
* const lazy = getLazyDecorator((serviceIdentifier) =>
* container.get(serviceIdentifier)
* );
*
* @injectable()
* class Foo {
* public get test() {
* return 'test';
* }
* }
*
* @injectable()
* class Bar {
* @lazy('foo')
* foo?: Foo;
* }
*
* container = createContainer({
* ServiceIdentifiers: new Map(),
* });
*
* const bar = container.get(Bar);
*
* container.bind('foo').to(Foo);
* expect(bar.foo?.test).toBe('test');
* ```
*/
export const getLazyDecorator =
(
getService: (
serviceIdentifier: ServiceIdentifier<unknown>,
target?: object
) => unknown
) =>
(serviceIdentifier: ServiceIdentifier<unknown>, enableCache = true) =>
(target: object, key: string | symbol) => {
function getter(this: object) {
if (enableCache && !Reflect.hasMetadata(METADATA_KEY.lazy, this, key)) {
const service = getService(serviceIdentifier, this);
if (service !== null) {
Reflect.defineMetadata(METADATA_KEY.lazy, service, this, key);
}
}
if (Reflect.hasMetadata(METADATA_KEY.lazy, this, key)) {
return Reflect.getMetadata(METADATA_KEY.lazy, this, key);
}
return getService(serviceIdentifier, this);
}
function setter(this: object, newVal: unknown) {
if (enableCache) {
Reflect.defineMetadata(METADATA_KEY.lazy, newVal, this, key);
} else {
console.warn(`
Disable cache and the property ${key.toString()} in class "${
this.constructor.name
}" instance failed to set value.
`);
}
}
// It should be compatible with the TS decorator and the babel decorator
return {
configurable: true,
enumerable: true,
get: getter,
set: setter,
} as any;
};