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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17382143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:06:15  来源:igfitidea点击:

Create a new object from type parameter in generic class

typescriptgenerics

提问by Javier Ros

I'm trying to create a new object of a type parameter in my generic class. In my class View, I have 2 lists of objects of generic type passed as type parameters, but when I try to make new TGridView(), TypeScript says:

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

Could not find symbol 'TGridView

找不到符号“TGridView”

This is the 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;
        }
    }
}

Can I create objects from a generic type?

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

采纳答案by Fenton

Because the compiled JavaScript has all the type information erased, you can't use Tto new up an object.

因为编译后的 JavaScript 已经删除了所有类型信息,所以您不能使用Tnew 对象。

You can do this in a non-generic way by passing the type into the constructor.

您可以通过将类型传递给构造函数来以非通用方式执行此操作。

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

You could extend this example using generics to tighten up the 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();

回答by blorkfish

To create a new object within generic code, you need to refer to the type by its constructor function. So instead of writing this:

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

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

You need to write this:

你需要这样写:

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

var classA: ClassA = activator(ClassA);

See this question: Generic Type Inference with Class Argument

看到这个问题: Generic Type Inference with Class Argument

回答by TadasPa

All type information is erased in JavaScript side and therefore you can't new up T just like @Sohnee states, but I would prefer having typed parameter passed in to constructor:

所有类型信息都在 JavaScript 端被删除,因此你不能像 @Sohnee 状态那样更新 T,但我更喜欢将类型参数传递给构造函数:

class A {
}

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

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

回答by jales cardoso

export abstract class formBase<T> extends baseClass {

  protected item = {} as T;
}

Its object will be able to receive any parameter, however, type T is only a typescript reference and can not be created through a constructor. That is, it will not create any class objects.

它的对象将能够接收任何参数,但是,类型 T 只是一个打字稿引用,不能通过构造函数创建。也就是说,它不会创建任何类对象。

回答by Jazib

I know late but @TadasPa's answer can be adjusted a little by using

我知道晚了,但@TadasPa 的答案可以通过使用进行一些调整

TCreator: new() => T

instead of

代替

TCreator: { new (): T; }

so the result should look like this

所以结果应该是这样的

class A {
}

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

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

回答by Christian Patzer

I was trying to instantiate the generic from within a base class. None of the above examples worked for me as they required a concrete type in order to call the factory method.

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

After researching for awhile on this and unable to find a solution online, I discovered that this appears to work.

在对此进行了一段时间的研究并且无法在网上找到解决方案后,我发现这似乎有效。

 protected activeRow: T = {} as T;

The pieces:

碎片:

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

...

...

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

All together

全部一起

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

That said, if you need an actual instance you should use:

也就是说,如果你需要一个实际的实例,你应该使用:

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

If you have arguments, you can use.

如果你有参数,你可以使用。

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

回答by Saturnus

This is what I do to retain type info:

这是我为保留类型信息所做的工作:

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

});

回答by JCK?del

Not quite answering the question, but, there is a nice library for those kind of problems: https://github.com/typestack/class-transformer(although it won't work for generic types, as they don't really exists at run-time (here all work is done with class names (which are classes constructors)))

不能完全回答这个问题,但是,对于这类问题,有一个不错的库:https: //github.com/typestack/class-transformer(尽管它不适用于泛型类型,因为它们实际上并不存在)在运行时(这里所有的工作都是用类名完成的(它们是类构造函数)))

For instance:

例如:

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

回答by Bojo

i use this: let instance = <T>{};it generally works EDIT 1:

我使用这个: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]);
  }
}

回答by EQuadrado

I'm late for the party but this is the way I got it working. For arrays we need do some tricks:

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

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

Use it like this:

像这样使用它:

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

It can be improved but worked for my needs!

它可以改进,但可以满足我的需求!