2013-06-29 12 views
62

Próbuję utworzyć nowy obiekt parametru typu w mojej klasie ogólnej. W mojej klasie „Widok” mam 2 Wykaz obiektów typu rodzajowego przekazywane jako parametry typu, ale gdy próbuję zrobić new TGridView() maszynopis mówi Could not find symbol 'TGridView'...Jak utworzyć nowy obiekt z parametru typu w klasie ogólnej w maszynopisie?

Jest to kod:

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; 
     } 
    } 
} 

mogę utworzyć obiekty z rodzaju ogólnego?

Odpowiedz

35

Ponieważ skompilowany skrypt JavaScript ma usunięte wszystkie informacje o typie, nie można użyć funkcji T do utworzenia obiektu.

Można to zrobić w sposób ogólny, przekazując typ do konstruktora.

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(); 

Można rozszerzyć ten przykład przy użyciu rodzajowych do zaostrzenia typy:

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

Jest super czyste i działa fantastycznie! –

79

Aby utworzyć nowy obiekt kodem rodzajowy, trzeba odwołać się do rodzaju jego funkcji konstruktora. Więc zamiast pisać to:

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

Musisz napisać tak:

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

var classA: ClassA = activator(ClassA); 

Zobacz na to pytanie: Generic Type Inference with Class Argument

+13

Nieco spóźniony (ale użyteczny) komentarz - jeśli twój konstruktor przyjmuje argumenty, to również 'new()' musi również - lub bardziej ogólny: 'type: {new (... args: any []): T;} ' – Rycochet

+4

Co to jest" IActivatable "? Nie mogę go znaleźć w żadnych dokumentach. – Yoda

+0

@ Yoda IActivatable to własny interfejs, zobacz inne pytanie: https://stackoverflow.com/questions/24677592/generic-type-inference-z -klass-argument – Jamie

12

Wszystkie informacje typu jest usuwane na stronie JavaScript i dlatego nie można new up T tak jak @Sohnee stwierdza, ale ja wolałbym mieć wpisany parametr przekazany do konstruktora:

class A { 
} 

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

var test = new B<A>(A); 
5
export abstract class formBase<T> extends baseClass { 

    protected item : T = {} as T; 
} 

Jego obiekt będzie mógł odbierać dowolny parametr, jednak typ T jest tylko referencją do maszynopisu i nie można go utworzyć za pomocą konstruktora. Oznacza to, że nie utworzy żadnych obiektów podklas.

+5

Przeczytaj [odpowiedź] i wyjaśnij, co robi ten kod. – CodeCaster

+2

* Strzeż się *, to może działać, ale wynikowy obiekt nie będzie typu 'T', więc wszelkie zmienne zdefiniowane w' T' mogą nie działać. –

+0

@ DanielOrmeño staram się wyjaśnić? Co przez to rozumiesz? Mój kod wydaje się działać przy użyciu tego fragmentu. Dzięki. – eestein

0

używam to: let instance = <T>{}; generalnie działa 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

To nie będzie faktycznie tworzyć instancji nowej instancji 'T', po prostu uczyni pusty obiekt, który TypeScript * uważa, że ​​* jest typu' T'. Możesz wyjaśnić swoją odpowiedź nieco bardziej szczegółowo. –

+0

Powiedziałem, że na ogół działa. Jest dokładnie tak, jak mówisz. Kompilator nie marudzi i masz ogólny kod. Możesz użyć tej instancji do ręcznego ustawienia żądanych właściwości bez potrzeby jawnego konstruktora. – Bojo

0

starałem się wystąpienia rodzajową od wewnątrz klasy bazowej. Żaden z powyższych przykładów nie zadziałał, ponieważ wymagał konkretnego typu, aby wywołać metodę fabryczną.

Po badaniu przez chwilę i nie mogąc znaleźć rozwiązania w Internecie, odkryłem, że to działa.

protected activeRow: T = {} as T; 

Kawałki:

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

...

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

Wszystko razem

export abstract class GridRowEditDialogBase<T extends DataRow> extends DialogBase{ 
     protected activeRow: T = {} as T; 
} 
Powiązane problemy