从json转换为类型的Typsescript对象 [英] Typsescript object from json casting to type
问题描述
在日常生活中,我需要从ajax中读取一些json并将其转换为某些Typed对象(包括其METHODS)。在互联网上,我发现并使用以下代码进行类型转换:
export class Obj
{
public static cast< T>(obj,type:{new(... args):T}):T
{
obj .__ proto__ = type.prototype;
返回对象;
}
}
例如,可以通过以下方式使用: / p>
let objFromJson = {id:666,名称: love};
let building:Building = null;
building = Obj.cast(objFromJson,Building);
//在这一点上,不调用Building的构造函数-这是
//正确,因为我们不创建对象,而只进行类型转换
building.test ('xx');
//在控制台上的这一点上,我们应该得到:
//> building.test a:xx,name:love
//因此对象 building确实具有Building Class
其中(示例来自 head)
出口类建筑物{ b
$ b构造函数(
公共ID:数字,
公共名称:字符串,
){
console.log('building.constructor:id:'+ id +',name'+名称);
}
公开测试(a){
console.log(’building.test a:’+ a +’,名称:’+ this.name);
}
}
其他信息:代替使用 cast< T>(obj,类型:{new(... args):T}):T
我们只能使用 cast< T>( obj,类型):T
,但我读到第二个版本会导致箭头功能出现问题( https:// stackoverflow.com/a/32186367/860099 )-我不明白为什么-?
问题:我不是真的了解Obj.cast方法的工作原理(例如,如何使用 ... args
进行调用)-有人可以解释吗?是否有人以类似方便的方式知道替代函数,但不是用于铸造而是用于CREATE对象(因此称为构造函数)以json数据形式生成json数据(例如 building = Obj.create(objFromJson,Building);
cast
的工作方式
Javascript使用原型继承。 __ proto __
属性代表当前对象的原型,这决定了给定对象的类型
对于使用对象文字创建的对象,此值为Object.prototype;对于使用数组文字创建的对象,此值为Array.prototype;对于函数,此值为Function.prototype。佛r使用new fun创建的对象,其中fun是JavaScript提供的内置构造函数之一(数组,布尔值,日期,数字,对象,字符串等-包括随着JavaScript的发展而添加的新构造函数),此值是总是很有趣。 对于使用
新乐趣
创建的对象,其中fun
是脚本中定义的函数,该值为fun.prototype的值。
因此,当您更改 __ proto __
更改对象的原型链,从而基本上更改其类型。
类型:{new(... args):T}
表示构造函数的方式在打字稿中。就像上面的引文所说,对于由函数(例如 type
)构造的对象, __ ptoto __
应该是<$函数的c $ c>原型。
因此,在设置 __ proto __
时, cast
基本上模拟了使用 type
构造函数作为参数传递来构造对象的事实。
这种方法的问题
问题在于构造函数实际上并没有被调用,您只是在模拟使用给定构造函数创建对象的事实。因此,构造函数中发生的任何初始化都不会执行。例如,箭头函数需要捕获 this
,因此它们不是在构造函数的 prototype
上设置,而是在构造函数
调用期间对象的实例,因此,如果不调用构造函数,则不会初始化箭头函数:
导出类Building {
private otherField:string;
构造函数(
公用ID:数字,
公用名称:字符串,
){
console.log('building.constructor:id:'+ id +' ,名称'+名称);
this.otherField = name + id;
// Ts在JS中添加了用于初始化箭头函数的代码,但是想法是一样的,这就是它发生的地方
}
public arrow =()= > {};
公开测试(a){
console.log(’building.test a:’+ a +’,名称:’+ this.name);
//如果使用强制转换,则下面的两个字段均未定义。
console.log(‘building.otherField’+ this.otherField +’,箭头:’+ this.arrow); }
}
替代 cast
另一种方法是创建该类的新实例并使用 Object.assign
来分配 json
对象的属性。乍一看,这似乎较慢,但是文档说更改 __ ptoto __
非常慢,不建议
根据现代JavaScript引擎如何优化属性访问的性质,更改对象的[[Prototype]]在每个浏览器和JavaScript引擎中都是非常缓慢的操作。
出口等级大楼{
public id:number;
公共名称:字符串;
构造函数(data:Partial< Building>){
Object.assign(this,data)
console.log('building.constructor:id:'+ this.id +',名称'+ this.name);
}
公开测试(a){
console.log(’building.test a:’+ a +’,名称:’+ this.name);
}
}
let objFromJson = {id:666,name: love};
let building:建筑物=新建筑物(objFromJson);
如果该类没有任何方法,也许您可以更改设计以使其没有,那么我只需要使用一个接口来输入JSON对象并继续使用原始JSON对象即可。
In daily life I need to read some json from ajax and cast it to some Typed object (including its METHODS). On internet I found and use following code to type casting:
export class Obj
{
public static cast<T>(obj, type: { new(...args): T} ): T
{
obj.__proto__ = type.prototype;
return obj;
}
}
As example It can be use in following way:
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
where (example is from '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);
}
}
Additional info: Instead of using cast<T>(obj, type: { new(...args): T} ): T
we can use just cast<T>(obj, type): T
but I read that second version will cause problem with arrow functions (https://stackoverflow.com/a/32186367/860099) - I don't understand why - ?
Questions: I not really understand how method Obj.cast works (and for instance how I can use ...args
on calling it) - can someone explain it? Do someone know alternative function but not for casting but for CREATE object (so call constructor) form json data in similar handy way (eg. building = Obj.create(objFromJson, Building);
How cast
works
Javascript uses prototypical inheritance. The __proto__
property, represents the prototype of the current object. This is what determines the type of the given object
For objects created using an object literal, this value is Object.prototype. For objects created using array literals, this value is Array.prototype. For functions, this value is Function.prototype. For objects created using new fun, where fun is one of the built-in constructor functions provided by JavaScript (Array, Boolean, Date, Number, Object, String, and so on — including new constructors added as JavaScript evolves), this value is always fun.prototype. For objects created using
new fun
, wherefun
is a function defined in a script, this value is the value of fun.prototype.
So when you change the __proto__
you change the prototype chain for the object basically changing it's type.
type: { new(...args): T}
it the way you can represent a constructor function in typescript. As the above quote says for an object constructed by a function (such as type
) the __ptoto__
should be the prototype
of the function.
So when setting the __proto__
, cast
basically simulates the fact that the object was constructed using the type
constructor function passed as a parameter.
Problems with this approach
The problems is that the constructor doesn't actually get invoked, you are just simulating the fact that the object was created using the given constructor. So any initialization that occurs in the constructor does not get executed. Arrow functions for example need to capture this
, so they are not set on the prototype
of the constructor function but rather on the instance of the object during the invocation of the constructor
so if the constructor does not invoked, arrow functions are not initialized:
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); }
}
Alternative to cast
An alternative would be to create a new instance of the class and use Object.assign
to assign the properties from the json
object. At first glance this may seem slower, but the documentation says changing the __ptoto__
is very slow and not recommended
Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine.
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);
If the class does not have any methods, and maybe you can change your design so it does not, then I would just use an interface to type the JSON object and keep using the original JSON object.
这篇关于从json转换为类型的Typsescript对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!