2013-06-29 10 views
62

Ich versuche, ein neues Objekt eines Typparameters in meiner generischen Klasse zu erstellen. In meiner Klasse „Ansicht“ Ich habe 2 Liste der Objekte von generischem Typ als Typ-Parameter übergeben, aber wenn ich versuche new TGridView() Typoskript zu machen, sagt Could not find symbol 'TGridView'...Wie erstellt man ein neues Objekt aus Typ-Parameter in generischer Klasse in Typoskript?

Dies ist der Code:

module AppFW { 
    // Represents a view 
    export class View<TFormView extends FormView, TGridView extends GridView> { 
     // The list of forms 
     public Forms: { [idForm: string]: TFormView; } = {}; 

     // The list of grids 
     public Grids: { [idForm: string]: TGridView; } = {}; 

     public AddForm(formElement: HTMLFormElement, dataModel: any, submitFunction?: (e: SubmitFormViewEvent) => boolean): FormView { 
      var newForm: TFormView = new TFormView(formElement, dataModel, submitFunction); 
      this.Forms[formElement.id] = newForm; 
      return newForm; 
     } 

     public AddGrid(element: HTMLDivElement, gridOptions: any): GridView { 
      var newGrid: TGridView = new TGridView(element, gridOptions); 
      this.Grids[element.id] = newGrid; 
      return newGrid; 
     } 
    } 
} 

Kann ich erstellen Objekte von einem generischen Typ?

Antwort

35

Da die kompilierten JavaScript, um alle die Typinformationen gelöscht hat, können Sie nicht T neue verwenden ein Objekt auf.

Sie können dies tun, in einer nicht-generische Art und Weise durch die Art in den Konstruktor übergeben.

class TestOne { 
    hi() { 
     alert('Hi'); 
    } 
} 

class TestTwo { 
    constructor(private testType) { 

    } 
    getNew() { 
     return new this.testType(); 
    } 
} 

var test = new TestTwo(TestOne); 

var example = test.getNew(); 
example.hi(); 

Sie könnten dieses Beispiel unter Verwendung von Generika erweitern, um die Typen zu straffen:

class TestBase { 
    hi() { 
     alert('Hi from base'); 
    } 
} 

class TestSub extends TestBase { 
    hi() { 
     alert('Hi from sub'); 
    } 
} 

class TestTwo<T extends TestBase> { 
    constructor(private testType: new() => T) { 
    } 

    getNew() : T { 
     return new this.testType(); 
    } 
} 

//var test = new TestTwo<TestBase>(TestBase); 
var test = new TestTwo<TestSub>(TestSub); 

var example = test.getNew(); 
example.hi(); 
+0

Das ist super sauber und funktioniert fantastisch! –

79

Um ein neues Objekt im generischen Code zu erstellen, müssen Sie durch seine Konstruktorfunktion auf den Typen verweisen. Anstatt also schreibe dies:

function activatorNotWorking<T extends IActivatable>(type: T): T { 
    return new T(); // compile error could not find symbol T 
} 

Sie müssen dies schreiben:

function activator<T extends IActivatable>(type: { new(): T ;}): T { 
    return new type(); 
} 

var classA: ClassA = activator(ClassA); 

diese Frage Siehe: Generic Type Inference with Class Argument

+13

Etwas spät (aber nützlich) Kommentar - wenn Ihr Konstruktor args dann die 'new()' muss auch - oder eine allgemeinere: 'Typ: {new (... args: any []): T;} ' – Rycochet

+4

Was ist' IAktivierbar'? Ich kann es in keiner Dokumentation finden. – Yoda

+0

@ Yoda IActivatable ist eine selbst erstellte Schnittstelle, siehe andere Frage: https://stackoverflow.com/questions/24677592/generic-type-inference-with-class-argument – Jamie

12

Alle Typinformationen in JavaScript Seite gelöscht und daher kann man nicht

class A { 
} 

class B<T> { 
    Prop: T; 
    constructor(TCreator: { new(): T; }) { 
     this.Prop = new TCreator(); 
    } 
} 

var test = new B<A>(A); 
: neue bis T wie @Sohnee Staaten, aber ich würde es vorziehen, Parameter, um Konstruktor übergeben eingegeben haben
5
export abstract class formBase<T> extends baseClass { 

    protected item : T = {} as T; 
} 

Sein Objekt der Lage sein wird, alle Parameter zu empfangen, jedoch Typ T ist nur eine Typoskript Referenz und kann nicht durch einen Konstruktor erstellt werden. Das heißt, es werden keine Unterklassenobjekte erstellt.

+5

Bitte lesen [antworten] und erklären, was dieser Code tut. – CodeCaster

+2

* * Vorsicht, dies funktionieren kann, aber das resultierende Objekt wird nicht von Typ sein 'T', so dass alle Getter/Setter definiert in' T' möglicherweise nicht. –

+0

@ DanielOrmeño pflege zu erklären? Was meinst du damit? Mein Code scheint mit diesem Snippet zu arbeiten. Vielen Dank. – eestein

0

Ich benutze diese: let instance = <T>{}; es funktioniert im Allgemeinen EDIT 1:

export class EntityCollection<T extends { id: number }>{ 
    mutable: EditableEntity<T>[] = []; 
    immutable: T[] = []; 
    edit(index: number) { 
    this.mutable[index].entity = Object.assign(<T>{}, this.immutable[index]); 
    } 
} 
+2

wird nicht wirklich eine neue Instanz von 'T' instanziiert, es wird nur ein leeres Objekt machen, das Typoskript * denkt * ist vom Typ' T'. Vielleicht möchten Sie Ihre Antwort etwas genauer erklären. –

+0

Ich sagte, es funktioniert im Allgemeinen. Es ist genau wie du sagst. Compiler jammert nicht und Sie haben generischen Code. Sie können diese Instanz verwenden, um die gewünschten Eigenschaften ohne expliziten Konstruktor manuell festzulegen. – Bojo

0

ich die generische aus einer Basisklasse zu instanziiert versuchte. Keines der obigen Beispiele funktionierte für mich, da sie einen konkreten Typ erforderten, um die Fabrikmethode zu nennen.

Nach einer Weile auf dieser Erforschung und nicht in der Lage, eine Lösung zu finden online, entdeckte ich, dass dies scheint zu funktionieren.

protected activeRow: T = {} as T; 

Die Stücke:

activeRow: T = {} <-- activeRow now equals a new object... 

...

as T; <-- As the type I specified. 

Alle zusammen

export abstract class GridRowEditDialogBase<T extends DataRow> extends DialogBase{ 
     protected activeRow: T = {} as T; 
} 
Verwandte Themen