web-dev-qa-db-ja.com

ジェネリッククラスの型パラメーターから新しいオブジェクトを作成する

ジェネリッククラスに型パラメーターの新しいオブジェクトを作成しようとしています。クラス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;
        }
    }
}

ジェネリック型からオブジェクトを作成できますか?

104
Javier Ros

コンパイルされた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();
63
Fenton

ジェネリックコード内に新しいオブジェクトを作成するには、コンストラクター関数で型を参照する必要があります。これを書く代わりに:

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

この質問を参照してください: クラス引数を使用した一般的な型推論

115
blorkfish

JavaScript側ではすべての型情報が消去されるため、@ Sohneeの状態のようにTを更新することはできませんが、型付きパラメーターをコンストラクターに渡すことをお勧めします。

class A {
}

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

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

  protected item = {} as T;
}

そのオブジェクトは任意のパラメーターを受け取ることができますが、タイプTはTypeScript参照のみであり、コンストラクターを介して作成することはできません。つまり、クラスオブジェクトは作成されません。

8
jales cardoso

基本クラス内からジェネリックをインスタンス化しようとしていました。上記の例はどれも、ファクトリメソッドを呼び出すために具体的な型を必要とするため、私にとってはうまくいきませんでした。

これについてしばらく調査したところ、オンラインで解決策を見つけることができなかったため、これが機能しているように見えることがわかりました。

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

ただし、実際のインスタンスが必要な場合は、次を使用する必要があります。

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

引数がある場合は、使用できます。

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

私はこれを使用します: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]);
  }
}
0
Bojo

質問に完全に答えているわけではありませんが、これらの種類の問題のための素敵なライブラリがあります: https://github.com/typestack/class-transformer (ただし、ジェネリック型は実行時に実際には存在しないため、動作しません(ここでは、すべての作業はクラスコンストラクターです))

例えば:

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