Создать новый объект из параметра типа в обобщенном классе

91

Я пытаюсь создать новый объект параметра типа в моем родовом классе. В моем классе View меня есть 2 списка объектов общего типа, передаваемых в качестве параметров типа, но когда я пытаюсь создать new TGridView(), TypeScript говорит:

Не удалось найти символ 'TGridView

Это код:

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

Могу ли я создавать объекты из универсального типа?

Теги:
generics

6 ответов

63
Лучший ответ

Поскольку скомпилированный JavaScript имеет всю информацию о типе, вы не можете использовать T для нового объекта.

Вы можете сделать это не общим способом, передав этот тип в конструктор.

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

Вы можете расширить этот пример с помощью дженериков, чтобы затянуть типы:

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

Чтобы создать новый объект в общем коде, вам нужно обратиться к типу с помощью его функции-конструктора. Поэтому вместо того, чтобы писать это:

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

Вам нужно написать следующее:

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

var classA: ClassA = activator(ClassA);

Смотрите этот вопрос: Общий вывод типа с аргументом класса

  • 23
    Несколько поздний (но полезный) комментарий - если ваш конструктор принимает аргументы, то new() тоже должен это сделать - или более общий: type: {new(...args : any[]): T ;}
  • 5
    Что такое IActivatable ? Я не могу найти это ни в одном документе.
Показать ещё 1 комментарий
20

Вся информация о типе стирается со стороны JavaScript, поэтому вы не можете создавать новые T так же, как и состояния @Sohnee, но я бы предпочел, чтобы этот параметр был введен в конструктор:

class A {
}

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

var test = new B<A>(A);
  • 0
    Это должен быть правильный ответ. Спасибо
8
export abstract class formBase<T> extends baseClass {

  protected item = {} as T;
}

Его объект сможет принимать любой параметр, однако тип T является только ссылкой на машинописный текст и не может быть создан с помощью конструктора. То есть он не будет создавать никаких объектов класса.

  • 6
    Пожалуйста, прочтите Как ответить и объясните, что делает этот код.
  • 5
    Помните , что это может работать, но результирующий объект не будет иметь тип T , поэтому любые методы получения / установки, определенные в T могут не работать.
Показать ещё 2 комментария
0

Я пытался создать экземпляр generic из базового класса. Ни один из приведенных выше примеров не работал у меня, поскольку для вызова метода factory требовался конкретный тип.

После некоторого исследования на этом и неспособного найти решение в Интернете, я обнаружил, что это работает.

 protected activeRow: T = {} as T;

Куски:

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

...

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

Все вместе

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

i использую это: let instance = <T>{}; он вообще работает 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]);
  }
}
  • 3
    Это на самом деле не будет создавать новый экземпляр T , а просто создаст пустой объект, который, как думает TypeScript, будет иметь тип T Вы можете объяснить свой ответ более подробно.
  • 0
    Я сказал, что это вообще работает. Это именно так, как вы говорите. Компилятор не ноет, и у вас есть общий код. Вы можете использовать этот экземпляр, чтобы вручную установить нужные свойства без необходимости явного конструктора.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню