7

How can I make the following work as expected in Typescript?

export class ProxyObject<T> {
    private _dataObject:T;
    constructor(dataObject?:T) {
        if (dataObject) {
            this.dataObject = dataObject;
        } else {
            //create a new instance of the generic <T> here
            this.dataObject = <T> new Object();
        }
    }
    set dataObject(dataObject:T) {
        this._dataObject = dataObject;
    }
    get dataObject():T {
        return this._dataObject;
    }
}

export class ClassA {
   myCoolProperty = "My Cool Property";
}

When I do the following:

export class MyObject extends ProxyObject<ClassA> {
}

And then:

var obj = new MyObject();
obj.dataObject.myCoolProperty === undefined

None of the ClassA properties or functions exist on the dataObject inside the MyObject as I was expecting. For instance, I expected dataObject to have myCoolProperty.

I'm trying to write a ProxyObject class that when extended it's DataObject storage is typed as the generic.

Thanks!!

Joe Firebaugh
  • 416
  • 1
  • 4
  • 10
  • My guess is that you need to do `obj.dataObject().myCoolProperty` because the generated javascript is probably making your public get property a function. – Pricey Apr 03 '15 at 17:44
  • I'm fairly confident that this code: `this.dataObject = new Object();` Doesn't actually create a new instance of the generic. The debugger shows dataObject as an empty Object. – Joe Firebaugh Apr 03 '15 at 17:56
  • sorry I got the wrong idea when reading that initially. There is this that might help you: https://www.stevefenton.co.uk/Content/Blog/Date/201407/Blog/Creating-TypeScript-Classes-Dynamically/ – Pricey Apr 03 '15 at 18:10
  • Possible duplicate of [How to create a new object from type parameter in generic class in typescript?](http://stackoverflow.com/questions/17382143/how-to-create-a-new-object-from-type-parameter-in-generic-class-in-typescript) – Louis Dec 23 '16 at 00:01

3 Answers3

4

Since typescript generics are implemented using type erasure T is not present at runtime, you can however pass the class in as a parameter to the base class constructor and then use it to create the new object.

export class DataObjectBase {
}

export class ProxyObject<T extends DataObjectBase> {
    private _dataObject: T;
    constructor(dataObject?: T, cls?: typeof DataObjectBase) {
        if (dataObject) {
            this.dataObject = dataObject;
        } else {
            //create a new instance of the generic <T> here
            this.dataObject = <T> new cls();
        }
    }
    set dataObject(dataObject: T) {
        this._dataObject = dataObject;
    }
    get dataObject(): T {
        return this._dataObject;
    }
}



export class ClassA {
    myCoolProperty = "My Cool Property";
}


export class MyObject extends ProxyObject<ClassA> {
    public constructor(dataObject?: ClassA) {
        super(dataObject, ClassA);
    }
}

new MyObject();
obj.dataObject.myCoolProperty !== undefined

This approach still requires a bit of code in the derived classes but at least it keeps all of the logic in the base class

The class DataObjectBase is necessary since if I would have used typeof Object instead, ClassA could not have fulfilled the signarure of the Object class (as in ClassA doesn't have the constructors Object has). DataObjectBase is a class with a single constructor with no arguments, which ClassA, although not explicitly deriving from it, fulfills.

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
0

The <T> new Object() doesn't create an instance of T. Try this:

class MyObject extends ProxyObject<ClassA> {
    constructor(dataObject?: ClassA) {
        if (dataObject) {
              super(dataObject);
          } else {
              //create a new instance of the generic <T> here
              super(new ClassA());
          }
    }
}
Paleo
  • 21,831
  • 4
  • 65
  • 76
  • This would work, just requires a little more code every time I extend the ProxyObject. The solution I posted is a little simpler IMHO. It's just too bad there's not an easy way to get a new instance of a Generic Type. Thanks! – Joe Firebaugh Apr 03 '15 at 18:14
0

I am open to some cooler answers to this question, but for now I just added a method that needs to be overloaded to return a new instance of the Generic.

export class ProxyObject<T> {

    private _dataObject:T;

    constructor(dataObject?:any) {
        if (dataObject) {
            this.dataObject = dataObject;
        } else {
            this.dataObject = this.createEmptyDataObject();
        }
    }

    protected createEmptyDataObject():T {
        throw new Error('You must overload the createEmptyDataObject() method on ProxyObject');
        return null;
    }

    set dataObject(dataObject:T) {
        this._dataObject = dataObject;
    }

    get dataObject():T {
        return this._dataObject;
    }
}

For instance, this is an example:

export class TabularRecord extends ProxyObject<TabularRecordDO> {

    createEmptyDataObject():TabularRecordDO {
        return new TabularRecordDO();
    }

    get record():any {
        return this.dataObject.record;
    }

    set record(record:any) {
        this.dataObject.record = record;
    }
}
Joe Firebaugh
  • 416
  • 1
  • 4
  • 10