【问题标题】:How to make a JS object *become* a TypeScript class instance如何使 JS 对象*成为* TypeScript 类实例
【发布时间】:2014-01-30 11:49:58
【问题描述】:

假设我有以下课程:

export class MyClass {
    str: string = '';

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

然后,在其他一些代码中:

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

我怎样才能将myObj 转换为MyClass 实例,所以以下行有效:

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

(请注意我不能更改myObj的创建,因为它是在另一个库中创建的)

***** 编辑:*****

我仍在寻找解决方案。主要问题是MyClass 引用了其他类,这些类可能引用了MyClass。这是一个完整的对象图,我正在尝试将其转换为 TypeScript 类。

我知道每个类和每个属性的类型,它与MyClass 上定义的类和属性完美匹配。

【问题讨论】:

    标签: javascript typescript


    【解决方案1】:

    可能是这样的:(我已将 MyClass 添加为 JS 代码)。

    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
    

    【讨论】:

      【解决方案2】:

      创建对象的其他库是否总是会从函数中返回该对象?您可以创建一个 .d.ts 定义文件,定义特定函数返回 MyClass。

      【讨论】:

      • 但是在这种情况下,MyClass 构造函数不会被执行,对吗?所以和使用__proto__有同样的问题。
      【解决方案3】:

      我认为最简单、最易读、代码行数最少的解决方案就是映射它:

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

      如果str 属性是强制性的,您可以将其设为构造函数参数并将其减少一行...

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

      更新

      如果您的程序中有许多具有相同属性的类,也许您可​​以将这些属性封装在一个类中。例如,如果所有类都有属性name, nickname, avatar,您可以将它们包装到Profile 类中。如果您需要从一个类中获取配置文件并将其添加到另一个类,这将为您提供一个要映射的属性。

      但是,您最了解您的用例,因此您可能对这个 JavaScript port of auto-mapper 感兴趣,如果属性都具有相同的名称,它应该很容易映射您的东西:

      【讨论】:

      • 我正在寻找一种不会创建新对象的通用方法。我对要转换的对象有很多引用,它们比仅使用一个属性str 复杂得多。
      • 属性名称是否总是匹配?
      • 是的,属性名称总是匹配的。
      【解决方案4】:

      发现了,现在我用的是下面的实用方法:

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

      以下调用通过分配正确的原型将myObj 转换为MyClass 实例:

      Util.become(myObj, MyClass);
      

      也许还有另一种更优雅的方式,不涉及使用__proto__

      【讨论】:

      • 如果其他类的构造函数中碰巧有代码,它不会被触发,使得这个解决方案不一定在所有情况下都是可移植的。您可能只想考虑将数据复制到新实例中。这样构造函数和属性设置器就有机会执行。
      • 你不应该摆弄__proto__。它在 ES5 及更低版本中是非标准的,即使在 ES6 中也不鼓励自己设置。
      【解决方案5】:

      由于您说您有很多具有未知属性列表的对象,我认为您不能为 MyClass 编写构造函数来接收任意对象并从其属性创建 MyClass 实例。因此,您无法将 myObj 有意义地“转换”为 MyClass 的实例。

      摆弄 myObj 的原型并不是一件好事。请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto 处的警告

      这让您可以使用鸭子类型:MyClass.foo.call(myObj); 这仅在 MyClass 的方法仅引用 myObj 上可用的属性时才有效,即您不希望 MyClass 的构造函数将任何其他属性设置为默认值,等等(因为在这种情况下 MyClass 构造函数实际上从未运行过)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-24
        • 1970-01-01
        • 1970-01-01
        • 2022-01-11
        • 2022-11-28
        • 1970-01-01
        相关资源
        最近更新 更多