从泛型类中的类型参数创建一个新对象

我试图在我的泛型类中创建一个类型参数的新对象。 在我的类View中,我有两个泛型类型的对象列表作为类型参数传递,但当我尝试创建new TGridView()时,TypeScript会说:

找不到符号'TGridView

这是代码:

module AppFW {
// Represents a view
export class View {
// 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;
}
}
}

我可以从泛型类型创建对象吗?

233885 次浏览

因为编译后的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();

要在泛型代码中创建新对象,需要通过其构造函数引用类型。所以不要这样写:

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

看到这个问题: 带类实参的泛型类型推断 < / p >

所有类型信息都在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只是一个typescript引用,不能通过构造函数创建。也就是说,它不会创建任何类对象。

我使用这个:let instance = <T>{}; 它通常是有效的 编辑1:< / p >

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

我试图从基类内实例化泛型。上面的例子都不适合我,因为它们需要一个具体的类型来调用工厂方法。

在研究了一段时间后,我无法在网上找到一个解决方案,我发现这似乎是可行的。

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

没有完全回答这个问题,但是,有一个很好的库可以解决这类问题: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

我知道晚了,但是@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);

这是我保留类型信息的方法:

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


});

我迟到了,但这是我让它工作的方式。对于数组,我们需要做一些技巧:

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

它可以改进,但为我的需要工作!

我是根据要求加上这个的,不是因为我认为它直接解决了问题。我的解决方案涉及一个表组件显示表从我的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的所有数据执行转换、验证和其他任何操作。希望它能为某些人提供一些见解。

这里有一个例子,如果你需要在构造函数中的参数:

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