从json转换到type的Typsescript对象

时间:2018-02-05 05:41:53

标签: json typescript casting

在日常生活中,我需要从ajax中读取一些json并将其转换为某些Typed对象(包括其METHODS)。在互联网上,我发现并使用以下代码来输入:

export class Obj 
{
    public static cast<T>(obj, type: { new(...args): T} ): T 
    {
        obj.__proto__ = type.prototype;
        return obj;
    }
}

例如可以按以下方式使用:

let objFromJson = { id: 666, name: "love" };
let building: Building = null; 
building = Obj.cast(objFromJson, Building);

// On this point constructor for Building is not call - this is
// correct because we not create object but only make type casting

building.test('xx');

// on this point on console we should get:
// > "building.test a:xx, name:love"

// so object 'building' indeed have methods of Building Class

其中(例如来自'head')

export class Building {

    constructor(
        public id: number,
        public name: string,
    ) {
        console.log('building.constructor: id:' + id + ', name' + name);
    }

    public test(a) {
        console.log('building.test a:' + a + ', name:' + this.name);
    }

}

其他信息:我们只使用cast<T>(obj, type: { new(...args): T} ): T而不是cast<T>(obj, type): T,但我读到第二个版本会导致箭头功能出现问题(https://stackoverflow.com/a/32186367/860099) - 我不明白为什么 - ?

问题:我真的不明白Obj.cast方法是如何工作的(例如我如何使用...args来调用它) - 有人可以解释一下吗?有人知道替代函数但不是为了铸造而是为了CREATE对象(所以调用构造函数)以类似的方式形成json数据(例如。building = Obj.create(objFromJson, Building);

1 个答案:

答案 0 :(得分:1)

cast如何运作

Javascript使用prototypical inheritance__proto__属性表示当前对象的原型。这是决定给定对象类型的原因

  

对于使用对象文字创建的对象,此值为Object.prototype。对于使用数组文字创建的对象,此值为Array.prototype。对于函数,此值为Function.prototype。对于使用new fun创建的对象,fun是JavaScript提供的内置构造函数之一(Array,Boolean,Date,Number,Object,String等等 - 包括作为JavaScript演变添加的新构造函数),此值为总是fun.prototype。 对于使用new fun创建的对象,其中fun是脚本中定义的函数,此值为fun.prototype的值。

因此,当你更改__proto__时,你改变了对象的原型链,基本上改变了它的类型。

type: { new(...args): T}它可以表示typescript中的构造函数。正如上面的引用所说的由函数构造的对象(例如type),__ptoto__应该是函数的prototype

因此,在设置__proto__时,cast基本上模拟了使用作为参数传递的type构造函数构造对象的事实。

此方法存在问题

问题是构造函数实际上没有被调用,你只是在模拟使用给定构造函数创建对象的事实。因此,构造函数中发生的任何初始化都不会被执行。例如,箭头函数需要捕获this,因此它们不会在构造函数的prototype上设置,而是在调用constructor期间设置在对象的实例上,因此如果没有调用构造函数,箭头函数没有初始化:

export class Building {
    private otherField : string;
    constructor(
        public id: number,
        public name: string,
    ) {
        console.log('building.constructor: id:' + id + ', name' + name);
        this.otherField = name+id;
        // Ts adds in the code for initializing arrow functions in JS, but the idea is the same, this is where it would happen
    }

    public arrow = ()=> {};
    public test(a) {
        console.log('building.test a:' + a + ', name:' + this.name);

        // both fields below will be undefined if cast was used.
        console.log('building.otherField' + this.otherField + ', arrow:' + this.arrow);         }
}

替代cast

另一种方法是创建类的新实例,并使用Object.assign分配json对象的属性。乍一看这似乎有点慢,但是文档说改变__ptoto__非常慢而且不推荐

  

根据现代JavaScript引擎如何在每个浏览器和JavaScript引擎中优化属性访问,非常慢的操作,改变对象的[[Prototype]]。

export class Building {
    public id: number;
    public name: string;
    constructor(data: Partial<Building>){
        Object.assign(this, data)
        console.log('building.constructor: id:' + this.id + ', name' + this.name);
    }

    public test(a) {
        console.log('building.test a:' + a + ', name:' + this.name);
    }

}
let objFromJson = { id: 666, name: "love" };
let building: Building = new Building(objFromJson);

如果该类没有任何方法,并且您可以更改您的设计,那么我只会使用一个接口来键入JSON对象并继续使用原始JSON对象。