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
Create a new object from type parameter in generic class
提问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 T
to new up an object.
因为编译后的 JavaScript 已经删除了所有类型信息,所以您不能使用T
new 对象。
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
回答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!
它可以改进,但可以满足我的需求!