Inversifyjs: wrapping constructor with custom decorator

Created on 16 Jun 2017  路  1Comment  路  Source: inversify/InversifyJS

Expected Behavior

I got a custom decorator that wraps the constructor to do other stuffs when instantiating code.
When I'm trying to inject stuffs into the constructor, everything is undefined.

Current Behavior

Injected stuffs are undefined

Steps to Reproduce (for bugs)

  1. Open code.
  2. run code
  3. log in constructor returns undefined
  4. Uncaught TypeError: Cannot read property 'say' of undefined
question

Most helpful comment

This issue is caused because InversifyJS adds some metadata to the constructor. You are creating a new constructor and the metadata is lost. You need to manually copy the metadata from the original constructor to the new constructor. We can use the reflect-metadata API:

const {Container, injectable, inject, named} = inversify

function logClass(target: any) {

  // save a reference to the original constructor
  var original = target;

  // a utility function to generate instances of a class
  function construct(constructor, args) {
    var c : any = function () {
      return new constructor(...args);
    }
    c.prototype = constructor.prototype;
    return new c();
  }

  // the new constructor behaviour
  var newConstructor : any = function (...args) {
    console.log("New: " + original.name);
    return construct(original, args);
  }

  // copy prototype so intanceof operator still works
  newConstructor.prototype = original.prototype;

  // Copy metadata
  var metadataKeys = Reflect.getMetadataKeys(target);
  metadataKeys.forEach((metadataKey) => {
      var metadataValue = Reflect.getMetadata(metadataKey, target);
      Reflect.defineMetadata(metadataKey, metadataValue, newConstructor);
  });

  // return new constructor (will override original)
  return newConstructor;
}

interface IPerson {
    say: () => void;
}

@injectable()
class Person implements IPerson {
    say() {
    console.log('hello folks');
  }
}

let personType = Symbol('Person');
let stuffType = Symbol('Stuff');

@injectable()
@logClass
class Stuff {
    constructor(@inject(personType) private person: IPerson) {
    console.log(person);
  }

  do() {
    this.person.say();
  }
}

let container = new Container();
container.bind<IPerson>(personType).to(Person);
container.bind<Stuff>(stuffType).to(Stuff);

let stuff = container.get<Stuff>(stuffType);
stuff.do(); // hello folks

>All comments

This issue is caused because InversifyJS adds some metadata to the constructor. You are creating a new constructor and the metadata is lost. You need to manually copy the metadata from the original constructor to the new constructor. We can use the reflect-metadata API:

const {Container, injectable, inject, named} = inversify

function logClass(target: any) {

  // save a reference to the original constructor
  var original = target;

  // a utility function to generate instances of a class
  function construct(constructor, args) {
    var c : any = function () {
      return new constructor(...args);
    }
    c.prototype = constructor.prototype;
    return new c();
  }

  // the new constructor behaviour
  var newConstructor : any = function (...args) {
    console.log("New: " + original.name);
    return construct(original, args);
  }

  // copy prototype so intanceof operator still works
  newConstructor.prototype = original.prototype;

  // Copy metadata
  var metadataKeys = Reflect.getMetadataKeys(target);
  metadataKeys.forEach((metadataKey) => {
      var metadataValue = Reflect.getMetadata(metadataKey, target);
      Reflect.defineMetadata(metadataKey, metadataValue, newConstructor);
  });

  // return new constructor (will override original)
  return newConstructor;
}

interface IPerson {
    say: () => void;
}

@injectable()
class Person implements IPerson {
    say() {
    console.log('hello folks');
  }
}

let personType = Symbol('Person');
let stuffType = Symbol('Stuff');

@injectable()
@logClass
class Stuff {
    constructor(@inject(personType) private person: IPerson) {
    console.log(person);
  }

  do() {
    this.person.say();
  }
}

let container = new Container();
container.bind<IPerson>(personType).to(Person);
container.bind<Stuff>(stuffType).to(Stuff);

let stuff = container.get<Stuff>(stuffType);
stuff.do(); // hello folks
Was this page helpful?
0 / 5 - 0 ratings

Related issues

AriaFallah picture AriaFallah  路  4Comments

inaiei picture inaiei  路  4Comments

remojansen picture remojansen  路  4Comments

remojansen picture remojansen  路  4Comments

Deviad picture Deviad  路  3Comments