Jonas Sourlier
Jonas Sourlier

Reputation: 14445

How to make a JS object *become* a TypeScript class instance

Say I have the following class:

export class MyClass {
    str: string = '';

    foo() {
        console.log(this.str);
    }
}

Then, in some other code:

var myObj = {
    str: 'Hello World';
}

How can I convert myObj into a MyClass instance, so the following line works:

myObj.foo();
// writes 'Hello World' to the console

(Please note that I cannot change the creation of myObj, because it's created in another library)

***** EDIT: *****

I'm still looking for a solution for this. The main problem is that MyClass has references to other classes, which maybe have references to MyClass. It's a whole object graph that I'm trying to convert to TypeScript classes.

Of every class and every property I know its type, and it matches perfectly with the classes and properties defined on MyClass.

Upvotes: 2

Views: 2680

Answers (5)

Fenton
Fenton

Reputation: 250972

I think the simplest solution, the most readable solution and the one with the fewest lines of code would be to just map it:

var myClass = new MyClass();
myClass.str = myObj.str;

myClass.foo(); 

If the str property is mandatory, you could make it a constructor parameter and reduce this by one line...

var myClass = new MyClass(myObj.str);

myClass.foo();

Update

If you have many classes in your program with the same properties, perhaps you could encapsulate those properties within a class. For example, if all the classes had properties name, nickname, avatar you might wrap them into a Profile class. This gives you one property to map should you need to take the profile from one class and add it to another.

However, you know your use case best, so you might be interested in this JavaScript port of auto-mapper, which should easily map your stuff if the properties all have the same names:

Upvotes: 1

Arnavion
Arnavion

Reputation: 3968

Since you say you have a lot of objects with an unknown list of properties, I take it you can't write a constructor for MyClass to take in an arbitrary object and create a MyClass instance from its properties. Therefore you can't meaningfully "convert" myObj to an instance of MyClass.

Fiddling with myObj's prototype is not a very nice thing to do. See the warning at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

That leaves you with using duck typing: MyClass.foo.call(myObj); This will only work if MyClass's methods only refer to properties available on myObj, i.e., you don't expect the constructor of MyClass to have set any other properties to default values, etc. (since in this case the MyClass constructor has effectively never run).

Upvotes: 0

Kyle
Kyle

Reputation: 33701

Is this other library that creates the object always going to return that object from the function? You could create a .d.ts definitions file that defines that the specific function returns MyClass.

Upvotes: 1

HMR
HMR

Reputation: 39280

Maybe something like this:(I've added the MyClass as JS code).

function becomeMyClass(o){
  var fn1=function(){
    var thing;
    MyClass.apply(arguments);
    //note: shallow copy only
    for(thing in o){
      if(Object.prototype.hasOwnProperty
        .call(o,thing)){
          this[thing]=o[thing];
      }
    }
  };
  fn1.prototype=Object.create(MyClass.prototype);
  return new fn1([].slice.call(arguments,1));
}
function MyClass(){
  this.str = "from myclass";
}
MyClass.prototype.foo=function(){
  console.log(this.str);
};
var myObj = {
  str:"from myObj"
}

myC = becomeMyClass(myObj);
console.log(myC.foo());//from myObj
console.log(myC instanceof MyClass);//true

Upvotes: 1

Jonas Sourlier
Jonas Sourlier

Reputation: 14445

Found it out, now I'm using the following utility method:

export class Util {
    static become(obj: any, newClass: any) {
        obj.__proto__ = (<any>(new newClass())).__proto__;
    }
}

The following call converts myObj into a MyClass instance by assigning the right prototype:

Util.become(myObj, MyClass);

Maybe there's another, more elegant way that doesn't involve the use of __proto__.

Upvotes: 0

Related Questions