簡體   English   中英

從通用 class 中的類型參數創建一個新的 object

[英]Create a new object from type parameter in generic class

我正在嘗試在我的通用 class 中創建一個類型參數的新 object。 在我的 class 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;
        }
    }
}

我可以從泛型類型創建對象嗎?

要在通用代碼中創建新對象,您需要通過其構造函數來引用類型。 所以,而不是寫這個:

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

看到這個問題: Generic Type Inference with Class Argument

因為編譯后的 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();

所有類型信息都在 JavaScript 端被刪除,因此你不能像 @Sohnee 狀態那樣更新 T,但我更喜歡將類型參數傳遞給構造函數:

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

它的對象將能夠接收任何參數,但是,類型 T 只是一個打字稿引用,不能通過構造函數創建。 也就是說,它不會創建任何類對象。

我知道晚了,但@TadasPa 的答案可以通過使用進行一些調整

TCreator: new() => T

代替

TCreator: { new (): T; }

所以結果應該是這樣的

class A {
}

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

var test = new B<A>(A);

我試圖從基類中實例化泛型。 上面的例子都不適合我,因為它們需要一個具體的類型來調用工廠方法。

在對此進行了一段時間的研究並且無法在網上找到解決方案后,我發現這似乎有效。

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

這是我為保留類型信息所做的工作:

class Helper {
   public static createRaw<T>(TCreator: { new (): T; }, data: any): T
   {
     return Object.assign(new TCreator(), data);
   }
   public static create<T>(TCreator: { new (): T; }, data: T): T
   {
      return this.createRaw(TCreator, data);
   }
}

...

it('create helper', () => {
    class A {
        public data: string;
    }
    class B {
        public data: string;
        public getData(): string {
            return this.data;
        }
    }
    var str = "foobar";

    var a1 = Helper.create<A>(A, {data: str});
    expect(a1 instanceof A).toBeTruthy();
    expect(a1.data).toBe(str);

    var a2 = Helper.create(A, {data: str});
    expect(a2 instanceof A).toBeTruthy();
    expect(a2.data).toBe(str);

    var b1 = Helper.createRaw(B, {data: str});
    expect(b1 instanceof B).toBeTruthy();
    expect(b1.data).toBe(str);
    expect(b1.getData()).toBe(str);

});

我是應要求添加的,不是因為我認為它直接解決了問題。 我的解決方案涉及一個表組件,用於顯示我的 SQL 數據庫中的表:

export class TableComponent<T> {

    public Data: T[] = [];

    public constructor(
        protected type: new (value: Partial<T>) => T
    ) { }

    protected insertRow(value: Partial<T>): void {
        let row: T = new this.type(value);
        this.Data.push(row);
    }
}

為了使用它,假設我的數據庫 VW_MyData 中有一個視圖(或表),並且我想為查詢返回的每個條目點擊我的 VW_MyData 類的構造函數:

export class MyDataComponent extends TableComponent<VW_MyData> {

    public constructor(protected service: DataService) {
        super(VW_MyData);
        this.query();
    }

    protected query(): void {
        this.service.post(...).subscribe((json: VW_MyData[]) => {
            for (let item of json) {
                this.insertRow(item);
            }
        }
    }
}

這比簡單地將返回值分配給 Data 是可取的原因是說我有一些代碼將轉換應用於其構造函數中的 VW_MyData 的某些列:

export class VW_MyData {
    
    public RawColumn: string;
    public TransformedColumn: string;


    public constructor(init?: Partial<VW_MyData>) {
        Object.assign(this, init);
        this.TransformedColumn = this.transform(this.RawColumn);
    }

    protected transform(input: string): string {
        return `Transformation of ${input}!`;
    }
}

這允許我對輸入​​到 TypeScript 的所有數據執行轉換、驗證和其他任何操作。 希望它為某人提供一些見解。

我用這個: let instance = <T>{}; 它通常有效 編輯 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]);
  }
}

不能完全回答這個問題,但是,對於這類問題,有一個不錯的庫: 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

我參加聚會遲到了,但這就是我讓它工作的方式。 對於數組,我們需要做一些技巧:

   public clone<T>(sourceObj: T): T {
      var cloneObj: T = {} as T;
      for (var key in sourceObj) {
         if (sourceObj[key] instanceof Array) {
            if (sourceObj[key]) {
               // create an empty value first
               let str: string = '{"' + key + '" : ""}';
               Object.assign(cloneObj, JSON.parse(str))
               // update with the real value
               cloneObj[key] = sourceObj[key];
            } else {
               Object.assign(cloneObj, [])
            }
         } else if (typeof sourceObj[key] === "object") {
            cloneObj[key] = this.clone(sourceObj[key]);
         } else {
            if (cloneObj.hasOwnProperty(key)) {
               cloneObj[key] = sourceObj[key];
            } else { // insert the property
               // need create a JSON to use the 'key' as its value
               let str: string = '{"' + key + '" : "' + sourceObj[key] + '"}';
               // insert the new field
               Object.assign(cloneObj, JSON.parse(str))
            }
         }
      }
      return cloneObj;
   }

像這樣使用它:

  let newObj: SomeClass = clone<SomeClass>(someClassObj);

它可以改進,但可以滿足我的需求!

如果您需要在構造函數中使用參數,這是示例:

class Sample {
    public innerField: string;

    constructor(data: Partial<Sample>) {
        this.innerField = data.innerField;
    }
}

export class GenericWithParams<TType> {
    public innerItem: TType;

    constructor(data: Partial<GenericWithParams<TType>>, private typePrototype: new (i: Partial<TType>) => TType) {
        this.innerItem = this.factoryMethodOnModel(data.innerItem);
    }

    private factoryMethodOnModel = (item: Partial<TType>): TType => {
        return new this.typePrototype(item);
    };
}

const instance = new GenericWithParams<Sample>({ innerItem : { innerField: 'test' }}, Sample);

在Typescript中,我使用Object來解析“單個對象”類型和“對象數組”類型

public class A<T>  
{   
   public Data:T = new Object() as T; 
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM