it-swarm.com.de

Erstellen Sie ein neues Objekt aus dem Typparameter in der generischen Klasse

Ich versuche, ein neues Objekt eines Typparameters in meiner generischen Klasse zu erstellen. In meiner Klasse View habe ich 2 Listen von Objekten des generischen Typs, die als Typparameter übergeben wurden, aber wenn ich versuche, new TGridView() zu erstellen, sagt TypeScript:

Symbol 'TGridView' konnte nicht gefunden werden

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 Objekte aus einem generischen Typ erstellen?

111
Javier Ros

Da in dem kompilierten JavaScript alle Typinformationen gelöscht wurden, können Sie T nicht verwenden, um ein Objekt neu zu erstellen.

Sie können dies auf nicht generische Weise tun, indem Sie den Typ an 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önnen dieses Beispiel mit Generika erweitern, um die Typen zu verfeinern:

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

Um ein neues Objekt in generischem Code zu erstellen, müssen Sie anhand der Konstruktorfunktion auf den Typ verweisen. Also anstatt dies zu schreiben:

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

Siehe diese Frage: Generic Type Inference with Class Argument

121
blorkfish

Alle Typinformationen werden in der JavaScript-Seite gelöscht und daher können Sie T nicht wie bei @Sohnee-Zuständen neu eingeben, aber ich würde es vorziehen, Parameter an den Konstruktor übergeben zu lassen:

class A {
}

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

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

  protected item = {} as T;
}

Das Objekt kann jeden Parameter empfangen. Typ T ist jedoch nur eine TypeScript-Referenz und kann nicht über einen Konstruktor erstellt werden. Das heißt, es werden keine Klassenobjekte erstellt.

8
jales cardoso

Ich habe versucht, das Generikum aus einer Basisklasse heraus zu instanziieren. Keines der obigen Beispiele hat für mich funktioniert, da für den Aufruf der Factory-Methode ein konkreter Typ erforderlich war.

Nachdem ich eine Weile darüber recherchiert hatte und online keine Lösung finden konnte, stellte ich fest, dass dies zu funktionieren scheint.

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

Das heißt, wenn Sie eine tatsächliche Instanz benötigen, sollten Sie verwenden:

export function getInstance<T extends Object>(type: (new (...args: any[]) => T), ...args: any[]): T {
      return new type(...args);
}


export class Foo {
  bar() {
    console.log("Hello World")
  }
}
getInstance(Foo).bar();

Wenn Sie Argumente haben, können Sie verwenden.

export class Foo2 {
  constructor(public arg1: string, public arg2: number) {

  }

  bar() {
    console.log(this.arg1);
    console.log(this.arg2);
  }
}
getInstance(Foo, "Hello World", 2).bar();
3

ich benutze das: let instance = <T>{}; es funktioniert generell 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]);
  }
}
0
Bojo

Nicht ganz die Frage beantworten, aber es gibt eine nette Bibliothek für diese Art von Problemen: https://github.com/typestack/class-transformer (obwohl es für generische Typen nicht funktioniert, da sie zur Laufzeit nicht wirklich existieren (hier wird alles mit Klassennamen (die Klassenkonstruktoren sind) gearbeitet)

Zum Beispiel:

import {Type, plainToClass, deserialize} from "class-transformer";

export class Foo
{
    @Type(Bar)
    public nestedClass: Bar;

    public someVar: string;

    public someMethod(): string
    {
        return this.nestedClass.someVar + this.someVar;
    }
}

export class Bar
{
    public someVar: string;
}

const json = '{"someVar": "a", "nestedClass": {"someVar": "B"}}';
const optionA = plainToClass(Foo, JSON.parse(json));
const optionB = deserialize(Foo, json);

optionA.someMethod(); // works
optionB.someMethod(); // works
0
JCKödel