Jquery TypeScript回调未在其签名中完全实例化类

Jquery TypeScript回调未在其签名中完全实例化类,jquery,ajax,json,typescript,Jquery,Ajax,Json,Typescript,我在TypeScript中有一个类,它对一个服务进行Ajax调用,然后返回一个JSON对象;非常标准的东西。我希望Ajax调用的成功处理程序将传入的JSON作为在TypeScript中定义的实例化类接受,也就是说,我在这里选择强类型Ajax。这主要是有效的。我发现我可以很好地接收传入数据,但是JSON响应中没有的类的任何成员(属性和方法)在运行时都不会出现在对象上。这里有一些代码来演示这个问题 module Samples { export class MyData {

我在TypeScript中有一个类,它对一个服务进行Ajax调用,然后返回一个JSON对象;非常标准的东西。我希望Ajax调用的成功处理程序将传入的JSON作为在TypeScript中定义的实例化类接受,也就是说,我在这里选择强类型Ajax。这主要是有效的。我发现我可以很好地接收传入数据,但是JSON响应中没有的类的任何成员(属性和方法)在运行时都不会出现在对象上。这里有一些代码来演示这个问题

module Samples {

    export class MyData {
        public A: number;
        public B: number;
        public get C(): number { return this.A + this.B; }
    }

    export class ClassDemo {
        constructor(){

            var ajaxSettings: JQueryAjaxSettings = {
                url: "/api/build/sample",  // Returns {"A":6,"B":4}
                type: "GET",
                dataType: "json",
                cache: false
            };

            var jqXhr: JQueryXHR = $.ajax(ajaxSettings);
            jqXhr.done(this.loadSucceeded);
            jqXhr.fail( /* ... */);
        }

        private loadSucceeded(data: MyData, text: string, jqXhr: JQueryXHR) {

            // 
            // Displays "6 + 4 = undefined"
            //
            alert(data.A + " + " + data.B + " = " + data.C);
        }
    }
}

var sample = new Samples.ClassDemo();

我认为这是有意义的,因为JQuery在运行时用JavaScript创建对象,而不是TypeScript,所以JQuery无法知道TypeScript类中的其他成员。我只是想知道有没有什么好办法?出于显而易见的原因,我希望避免实例化自己的类,并手动将传入数据中的值复制到该类中。

我不认为有什么办法可以让jQuery自动将值作为
MyData
类的实例提供,而不仅仅是一个原始对象。因为最终,jQuery所做的就是使用
JSON.parse()
反序列化JSON文本,并将
done()
回调传递给结果JS对象。JSON不支持说对象是类的实例

您必须为AJAX请求创建一个拦截器,该拦截器接收返回的原始对象并为您构造一个类实例。这可以按类/请求单独完成,或者如果您让数据返回要实例化的类,并且每个类的构造函数都接受这样的对象,则可以全局执行。然后,您可以编写一个通用拦截器,将原始对象转换为正确类的实例

当然,您的服务器代码要么必须知道客户端类型,要么必须将类型指定为请求的一部分,并在响应中返回类型。

如果我简化示例(删除AJAX调用,但保留将简单数据对象转换为
MyData
实例这一重要问题),您可以通过将普通数据映射到新的
MyData
实例来解决此问题

例如,我使用了静态方法。您可以在这里选择自己的图案。也许您会接受
A
B
作为构造函数参数,或者使用映射器类或应用您认为最适合的任何模式:

静态法 建造师
您甚至可以通过在任意两个对象的“左”和“右”参数中查找匹配的属性来自动将属性映射到新实例,尽管这比普通映射更抽象。

感谢大家的解释和建议。我想我最终选择了它们的混合体,这就是为什么我要给出我自己的答案

我已经在实际代码中的一些地方实现了我描述的模式,所有涉及的类都不止两个成员。在开发的这个阶段,它们也会发生变化,因此创建一个通用帮助器类来将成员值从一个对象映射到另一个对象是有意义的。在我的场景中,将此映射器作为构造函数的一部分调用也是有意义的,因为某些类具有一些额外的设置操作。构造函数现在获取类的现有实例的可选参数,以从中复制数据

结果是这样的。最后一个演示案例是我的代码中最常用的模式

module Samples {

    export class Helpers {
        public static Recreate<T>(typeT: { new (): T; }, sourceObj: T, targetObj?: T) {

            if (targetObj == null) {
                targetObj = new typeT();
            }

            for (var property in sourceObj) {
                targetObj[property] = sourceObj[property];
            }

            return targetObj;
        }
    }

    export class MyData {

        constructor(fromObj?: MyData) {
            this.C = 0;

            if (fromObj != null) {
                Helpers.Recreate(MyData, fromObj, this);
            }

            this.C += 2;
        }

        public A: number;
        public B: number;
        public C: number;
        public get D(): number { return this.A + this.B + this.C; }
    }

    export class ClassDemo {
        constructor() {

            var ajaxSettings: JQueryAjaxSettings = {
                url: "/api/build/sample", // Returns {"A":6,"B":4}
                type: "GET",
                dataType: "json",
                cache: false
            };

            var jqXhr: JQueryXHR = $.ajax(ajaxSettings);
            jqXhr.done(this.loadSucceeded);
            jqXhr.fail( /* ... */);
        }

        private loadSucceeded(data: MyData, text: string, jqXhr: JQueryXHR) {

            // Illustrates the problem that MyData.C and MyData.D are undefined even though the type is in the signature
            // Displays "6 + 4 + undefined = undefined"
            alert(data.A + " + " + data.B + " + " + data.C + " = " + data.D);

            // The default output for a brand new MyData object; undefined and NaN values are as expected
            // Displays "undefined + undefined + 2 = NaN"
            var plainData: MyData = new MyData();
            alert(plainData.A + " + " + plainData.B + " + " + plainData.C + " = " + plainData.D);

            // This creates a real instance of MyData using the helper method directly and produces the right output
            // Displays "6 + 4 + 2 = 12"
            var recreatedData: MyData = Helpers.Recreate(MyData, data);
            alert(recreatedData.A + " + " + recreatedData.B + " + " + recreatedData.C + " = " + recreatedData.D);

            // This uses the helper method located in the constructor to rebuild 'data' from itself and produces the right output
            // Displays "6 + 4 + 2 = 12"
            data = new MyData(data);
            alert(data.A + " + " + data.B + " + " + data.C + " = " + data.D);
        }
    }
}

var sample = new Samples.ClassDemo();
模块示例{
导出类助手{
公共静态重新创建(typeT:{new():T;},sourceObj:T,targetObj?:T){
if(targetObj==null){
targetObj=新类型();
}
for(sourceObj中的var属性){
targetObj[property]=sourceObj[property];
}
返回targetObj;
}
}
导出类MyData{
构造函数(fromObj?:MyData){
这个C=0;
if(fromObj!=null){
重新创建(MyData,fromObj,this);
}
这个。C+=2;
}
公众A:数字;
公共B:数字;
公共C:数字;
public get D():number{返回this.A+this.B+this.C;}
}
导出类类演示{
构造函数(){
var ajaxSettings:JQueryAjaxSettings={
url:“/api/build/sample”,//返回{“A”:6,“B”:4}
键入:“获取”,
数据类型:“json”,
缓存:false
};
var jqXhr:JQueryXHR=$.ajax(ajaxSettings);
jqXhr.done(this.loadsuccessed);
jqXhr.fail(/*…*/);
}
私有加载成功(数据:MyData,文本:string,jqXhr:JQueryXHR){
//说明了MyData.C和MyData.D未定义的问题,即使类型在签名中
//显示“6+4+未定义=未定义”
警报(data.A+“+”+data.B+“+”+data.C+“=”+data.D);
//全新MyData对象的默认输出;未定义值和NaN值与预期值相同
//显示“未定义+未定义+2=NaN”
var plainData:MyData=newmydata();
警报(plainData.A+“+”+plainData.B+“+”+plainData.C+“=”+plainData.D);
//这将直接使用helper方法创建MyData的真实实例,并生成正确的输出
//显示“6+4+2=12”
var recreatedData:MyData=Helpers.Recreate(MyData,data);
警报(recreatedData.A+“+”+recreatedData.B+“+”+recreatedData.C+“=”+recreatedData.D);
//这将使用构造函数中的helper方法从自身重建“数据”,并生成正确的输出
module Samples {

    export class MyData {
        constructor(public A: number, public B:number){}

        public get C(): number { return this.A + this.B; }
    }

    export class ClassDemo {
        constructor(){
            var result = { A: 1, B: 2};
            this.loadSucceeded(new MyData(result.A, result.B));
        }

        private loadSucceeded(data: MyData) {
            alert(data.A + " + " + data.B + " = " + data.C);
        }
    }
}

var sample = new Samples.ClassDemo();
module Samples {

    export class Helpers {
        public static Recreate<T>(typeT: { new (): T; }, sourceObj: T, targetObj?: T) {

            if (targetObj == null) {
                targetObj = new typeT();
            }

            for (var property in sourceObj) {
                targetObj[property] = sourceObj[property];
            }

            return targetObj;
        }
    }

    export class MyData {

        constructor(fromObj?: MyData) {
            this.C = 0;

            if (fromObj != null) {
                Helpers.Recreate(MyData, fromObj, this);
            }

            this.C += 2;
        }

        public A: number;
        public B: number;
        public C: number;
        public get D(): number { return this.A + this.B + this.C; }
    }

    export class ClassDemo {
        constructor() {

            var ajaxSettings: JQueryAjaxSettings = {
                url: "/api/build/sample", // Returns {"A":6,"B":4}
                type: "GET",
                dataType: "json",
                cache: false
            };

            var jqXhr: JQueryXHR = $.ajax(ajaxSettings);
            jqXhr.done(this.loadSucceeded);
            jqXhr.fail( /* ... */);
        }

        private loadSucceeded(data: MyData, text: string, jqXhr: JQueryXHR) {

            // Illustrates the problem that MyData.C and MyData.D are undefined even though the type is in the signature
            // Displays "6 + 4 + undefined = undefined"
            alert(data.A + " + " + data.B + " + " + data.C + " = " + data.D);

            // The default output for a brand new MyData object; undefined and NaN values are as expected
            // Displays "undefined + undefined + 2 = NaN"
            var plainData: MyData = new MyData();
            alert(plainData.A + " + " + plainData.B + " + " + plainData.C + " = " + plainData.D);

            // This creates a real instance of MyData using the helper method directly and produces the right output
            // Displays "6 + 4 + 2 = 12"
            var recreatedData: MyData = Helpers.Recreate(MyData, data);
            alert(recreatedData.A + " + " + recreatedData.B + " + " + recreatedData.C + " = " + recreatedData.D);

            // This uses the helper method located in the constructor to rebuild 'data' from itself and produces the right output
            // Displays "6 + 4 + 2 = 12"
            data = new MyData(data);
            alert(data.A + " + " + data.B + " + " + data.C + " = " + data.D);
        }
    }
}

var sample = new Samples.ClassDemo();