简体   繁体   中英

Instantiate generic object from type of

I found this code to instantiate a new object T with a parameter :

create<T>(type: { new(p: any): T }, val: any): T {
    return new type(val);
  }

My goal is to have a base class which I can inherit to init my array properties :

export class Base {
  create<T>(type: { new(p: any): T }, val: any): T {
    return new type(val);
  }

  init<T>(propertyArray: T[], input: any): void {
    if (propertyArray == null) {
      propertyArray = [];
      for (let i in input) {
        let tmpObj = this.create(T, i);
        propertyArray.push(tmpObj);
      }
    }
  }
}

class A extends Base {

  private _prop: B[];

  get prop() {
    return this._prop;
  }
  set prop(value: any) {
    this.init(this._prop, value);
  }

  constructor(obj: any) {
    super(obj);
    this.prop = obj && obj.item || null;
  }
}

But in this sample let tmpObj = this.create(T, i); I get this error :

'T' only refers to a type, but is being used as a value here.

How can I correct this to work ?

Thank you

I'm not sure how the rest of your code is supposed to work, but you have to pass in a constructor of a type to be able to construct an instance of the type.


Let's examine the signature of the create() function:

declare function create<T>(type: { new(p: any): T }, val: any): T;

This is a generic function that works for any type T the caller wants. The (unfortunately named) type parameter is a constructor object that you call with new , and which returns a value of type T . It is an object that exists at runtime, not a type that exists only at design/compile time.

It is a confusing but useful practice to use the same name for a class constructor object and the type of an instance of the class:

class Foo {};

This creates both a class constructor object named Foo that exists at runtime, and an instance type named Foo that exists at design time:

const foo: Foo = new Foo();

The first Foo is the type, and the second Foo is the class constructor. This gets transpiled into something like:

var foo = new Foo();

Notice that the type named Foo is completely gone, since it exists only at design time. The point is: even though you often have constructor objects with the same name as types, they are not interchangeable. A generic type parameter is a type, not a constructor object.


Okay now let's see what you're passing into create() :

this.create(T, i)

Hmm, what's T ? It's the type parameter of the generic init() method. So it is only the name of a design-time type, not the name of a runtime object. This can never work. Instead, as above, you need to pass in a constructor object of the type { new(p: any): T } . But you don't have one of those, unless you add it to the list of parameters to init() :

init<T>(ctor: { new(p: any): T }, propertyArray: T[], input: any): void {
  // ...
  this.create(ctor, i);
  // ...
}

Now it will compile, although you will have to pass in a constructor for B in your prop setter. Not that I know what B is or if you have a constructor for it. And not that I know why the init() method only does something if the propertyArray parameter is null , so that the prop setter can only work once. And not that I know why you enumerate the keys of input and use them as arguments to whatever constructor you have for B . And... well, I'm obviously confused by the rest of your code. Suffice it to say that you need to pass in a constructor of T instead of T before you can proceed.

Hope that helps; good luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM