How to use InversifyJS to inject named constructor parameters? - javascript

I'm new to Inversify but want to utilise it in the codebase I'm working in. In this codebase, the dependencies are injected via an args object as a means to simulate named constructor parameters. What would be the best way to decorate this object for injection with Inversify, since I'm not able to simply do something like constructor(#inject("FooService") fooService, #inject("BarService") barService)?
interface FooService {
...
}
interface BarService {
...
}
type FooBarArgs = {
fooService: FooService;
barService: BarService;
}
#injectable()
class FooBar
{
private fooService: FooService;
private barService: BarService;
constructor(args:FooBarArgs) {
this.fooService = args.fooService;
this.barService = args.barService;
}
}

Related

Is dependeny injection possible when using the new operator?

Actually I want to implement some sort of strategy pattern. More precisely, I want to decide on runtime which class to instanciate.
Of course this is pretty simple.
if(...) {
this.service = new ServiceA();
} else {
this.service = new ServiceB();
}
But what if ServiceA and ServiceB use dependency injection in their classes? Do I have to pass those instances or is there a nicer way to let nest inject them automatically?
You can inject both services and then assign them to your variable dynamically:
service: Service;
constructor(private serviceA: ServiceA, private serviceB: ServiceB) {}
dynamicMethod() {
if (...) {
this.service = this.serviceA;
} else {
this.service = this.serviceB;
}
}
If the condition can be evaluated on startup (e.g. environment variabels) you can use a custom provider instead.

Why method added through decoration is not accessible

Suppose that I have a Directive decorator which adds a static method to it's target called factory:
function Directive<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
static factory(...args): ng.IDirectiveFactory {
const c: any = () => {
return constructor.apply(this, args);
};
c.prototype = constructor.prototype;
return new c(...args);
}
};
}
I also add the type via an interface:
interface BaseDirective extends ng.IDirective {
factory(): ng.IDirectiveFactory;
}
Why in my class declaration of:
#Directive
class FocusedDirective implements BaseDirective {....
I get a Class 'FocusedDirective' incorrectly implements interface 'BaseDirective'.
Property 'factory' is missing in type 'FocusedDirective'.
Am I wrong to expect from #Directive to add this missing property for me?
Decorators can't change the type of the class, you can invoke your decorator as a function and store the new class which will contain the method and use the new class instead of the original:
const FocusedDirectiveWithDirective = Directive(FocusedDirective);
You can do away with the intermediate class altogether by using class expressions:
const FocusedDirective = Directive(class implements BaseDirective{
});
You have two problems. The first has little to do with decorators: factory is a static method in your implementation, but a regular method in your interface:
interface BaseDirective {
factory(): ng.IDirectiveFactory;
}
That's going to be a problem for you. For now I'm going to convert the implementation to a regular method, since it's simpler to implement.
function Directive<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
factory(...args: any[]): ng.IDirectiveFactory {
const c: any = () => {
return constructor.apply(this, args);
};
c.prototype = constructor.prototype;
return new c(...args);
}
};
}
The second issue: decorators do not mutate the class signature the way you're expecting. This is an oft-requested feature and there are some interesting issues around why it's not a simple problem to solve. Importantly, it's not easy to figure out how to support having the implementation of the class refer to the mutated type. In your case: would the stuff inside your {.... know about factory() or not? Most people seem to expect it would, but the decorator hasn't been applied yet.
The workaround is not to use decorator syntax at all, but instead to use the decorator function as a regular function to create a new class. The syntax looks like this:
class FocusedDirective extends Directive(class {
// any impl that doesn't rely on factory
prop: string = "hey";
foo() {
this.prop; // okay
// this.factory(); // not okay
}
}) implements BaseDirective {
// any impl that relies on factory
bar() {
this.prop; // okay
this.foo(); // okay
this.factory(); // okay
}
}
This also solves the "does the implementation know about the decorator" issue, since the stuff inside the decorator function does not, and the stuff outside does, as you see above.
Back to the static/instance issue. If you want to enforce a constraint on the static side of a class, you can't do it by having the class implement anything. Instead, you need to enforce the static side on the class constructor itself. Like this:
interface BaseDirective {
// any actual instance stuff here
}
interface BaseDirectiveConstructor {
new(...args: any[]): BaseDirective;
factory(): ng.IDirectiveFactory;
}
class FocusedDirective extends Directive(class {
// impl without BaseDirectiveConstructor
}) implements BaseDirective {
// impl with BaseDirectiveConstructor
bar() {
FocusedDirective.factory(); // okay
}
}
function ensureBaseDirectiveConstructor<T extends BaseDirectiveConstructor>(t: T): void {}
ensureBaseDirectiveConstructor(FocusedDirective);
The ensureBaseDirectiveConstructor() function makes sure that the FocusedDirective class constructor has a static factory() method of the right type. That's where you'd see an error if you failed to implement the static side.
Okay, hope that helps. Good luck.

Proxy cannot get method in extended class

I am trying to use a Proxy, and I am having issues. I have a class like so:
export class Builder {
public doSomething(...args: (string | number | Raw | Object)[]): this {
// Do stuff
return this
}
}
export class ModelBase extends Builder {
protected _items = {}
}
export class Model extends ModelBase {
public constructor(options?: ModelSettings) {
super(options)
return new Proxy(this, {
get: function (target, property) {
return target._items[property] || target
}
})
}
public static create() {
return new this()
}
}
I then extend Model like so:
export class MyClass extends Model {
public constructor() {
super({/* Some options go here */})
// Do some stuff
}
public static getItems() {
let t = this.create()
t.doSomething()
}
}
Then I call getItems() which creates an instance of the class.
MyClass.getItems()
When I run this, I get the error:
TypeError: t.doSomething is not a function
Where doSomething() is within the class ModelBase. If I comment out the Proxy things work as usual. So, I would like to know why I can't access the parent class.
Your proxy is trying to find target._items.doSomething, but that doesn't exist. target.doSomething does exist, but it's not looking for that. As a result, it falls back to returning target, which is the object as a whole. The object, is not a function, and thus the error.
As for how to fix this, that depends on what you're trying to achieve with the proxy. If there's a limited set of properties that the proxy should be paying attention to, you could check those explicitly. Or more generally you might be able to check whether target[property] exists, then fall back to target._items[property] and only then fall back to target. Again, it depends on what you're trying to achieve.

Static function inheritance

I've created a few classes as Controllers for my routes in Node.js and I'd like them to be able to work on a singleton basis (not required, it's an extra)
Main Controller class
class RouteController {
static getInstance() {
if (!RouteController.singleton) {
RouteController.singleton = new RouteController();
}
return RouteController.singleton;
}
}
Now I want to create a route for example viewing posts and I want that class to have the singleton pattern as well, but not cop
PostController class
class PostController extends RouteController {
}
If I do this and check it console.log(RouteController.getInstance() === PostController.getInstance()) it returns true when I want each (sub)class to have their own singleton instance.
How can I do this correctly?
One simple way would be to see if your singleton property is an instance of this. This will work even if you call RouteController.getInstance() before doing so on any of the derived classes.
class RouteController {
log() {
return 'Route';
}
static getInstance() {
if (!(this.singleton instanceof this)) {
// Only called twice so you can verify it's only initialized
// once for each class
console.log('Creating singleton...');
this.singleton = new this();
}
return this.singleton;
}
}
class PostController extends RouteController {
log() {
return 'Post';
}
}
console.log(RouteController.getInstance().log());
console.log(PostController.getInstance().log());
console.log(RouteController.getInstance().log());
console.log(PostController.getInstance().log());
The normal rules of functions apply. Hence you can use this inside the function to refer to the object the method was invoked on (the constructor function itself in this case):
class RouteController {
static getInstance() {
if (!this.singleton) {
this.singleton = new this();
}
return this.singleton;
}
}
However, this will still not work if RouteController.getInstance() is called directly, because then all subclasses inherit a static singleton property, causing the !this.singleton test to fail for each subclass.
There are multiple ways to solve this. One would be to use getOwnProperty instead of accessing it directly:
if (!this.getOwnProperty('singleton')) {
}

How to convert decorator #action to non-decorator action in MobX

I am removing decorators from my React Native app (too many issues with babel) and my actions are not working (the contained function does not run).
I'm translating class actions e.g.
class MyStore {
//...
#action
myAction(param) {
//...
}
}
To
class MyStore {
//...
myAction(param) {
action("Perform action with param", (param) => {
//...
})
}
}
What's the correct way to convert a class #action to the non-decorator form?
You can define action as
class MyStore {
//...
myAction = action(param => {
//...
});
}
or use runInAction()
class MyStore {
//...
myAction(param) {
runInAction(() => {
//...
})
}
}
What's the correct way to convert a class #action to the non-decorator form?
Decorators evaluate to function calls at runtime, so simply calling them manually would be the most straightforward thing to do. #action is a method decorator, and method decorators take the following arguments at runtime:
The class prototype for instance methods (constructor function for static methods)
The method name (property key)
The property descriptor of the method
With that in mind, you can simply do:
class MyStore {
myAction(param) {
// ...
}
}
// Apply the #action decorator manually:
action(MyStore.prototype, "myAction");
or:
action(MyStore.prototype, "myAction", Object.getOwnPropertyDescriptor(MyStore.prototype, "myAction"));
If you do this immediately after the class declaration, the result should be completely identical to that by using decorators, without having to use the decorator syntax.

Categories

Resources