J'essaie de créer un nouvel objet d'un paramètre de type dans ma classe générique. Dans ma classe View
, j'ai 2 listes d'objets de type générique passés en paramètres de type, mais lorsque j'essaie de créer new TGridView()
, TypeScript dit:
Impossible de trouver le symbole 'TGridView
C'est le 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;
}
}
}
Puis-je créer des objets à partir d'un type générique?
Parce que le code JavaScript compilé a toutes les informations de type effacées, vous ne pouvez pas utiliser T
pour créer un nouvel objet.
Vous pouvez le faire de manière non générique en transmettant le type au constructeur.
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();
Vous pouvez étendre cet exemple en utilisant des génériques pour affiner les types:
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();
Pour créer un nouvel objet dans le code générique, vous devez faire référence au type par sa fonction constructeur. Donc au lieu d'écrire ceci:
function activatorNotWorking<T extends IActivatable>(type: T): T {
return new T(); // compile error could not find symbol T
}
Vous devez écrire ceci:
function activator<T extends IActivatable>(type: { new(): T ;} ): T {
return new type();
}
var classA: ClassA = activator(ClassA);
Voir cette question: Inférence de type générique avec argument de classe
Toutes les informations de type sont effacées du côté JavaScript. Par conséquent, vous ne pouvez pas créer de nouvelle T comme les états @Sohnee, mais je préférerais que le paramètre saisi soit passé au constructeur:
class A {
}
class B<T> {
Prop: T;
constructor(TCreator: { new (): T; }) {
this.Prop = new TCreator();
}
}
var test = new B<A>(A);
export abstract class formBase<T> extends baseClass {
protected item = {} as T;
}
Son objet pourra recevoir n’importe quel paramètre. Cependant, le type T n’est qu’une référence TypeScript et ne peut pas être créé via un constructeur. Autrement dit, il ne créera aucun objet de classe.
J'essayais d'instancier le générique à partir d'une classe de base. Aucun des exemples ci-dessus ne fonctionnait pour moi car ils nécessitaient un type concret pour appeler la méthode factory.
Après avoir recherché pendant un certain temps à ce sujet et incapable de trouver une solution en ligne, j'ai découvert que cela semblait fonctionner.
protected activeRow: T = {} as T;
Les morceaux:
activeRow: T = {} <-- activeRow now equals a new object...
...
as T; <-- As the type I specified.
Tous ensemble
export abstract class GridRowEditDialogBase<T extends DataRow> extends DialogBase{
protected activeRow: T = {} as T;
}
Cela dit, si vous avez besoin d’une instance réelle, vous devez utiliser:
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();
Si vous avez des arguments, vous pouvez utiliser.
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();
j'utilise ceci: let instance = <T>{};
cela fonctionne généralement 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]);
}
}
Pas tout à fait de réponse à la question, mais il existe une belle bibliothèque pour ce genre de problèmes: https://github.com/typestack/class-transformer (bien que cela ne fonctionne pas pour les types génériques, comme ils n'existent pas vraiment à l'exécution (ici tout le travail est fait avec des noms de classes (qui sont des constructeurs de classes)))
Par exemple:
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