简体   繁体   English

如何限制泛型类型具有 new()?

[英]How to constraint a generic type to have new()?

I want to have a function like:我想要一个像这样的功能:

createEntity<TEntity>(): TEntity {
    return new TEntity();
}

In C#, we could write:在 C# 中,我们可以这样写:

void TEntity CreateEntity<TEntity>() where TEntity : new()

How can I do this in TypeScript?我如何在 TypeScript 中执行此操作?

The only way shown in the handbook to do something similar to this is to send the class you want to initialize as a parameter to the factory method, and describe it's constructor using the new keyword. 手册中显示的执行类似操作的唯一方法是将要初始化的类作为参数发送给工厂方法,并使用new关键字描述它的构造函数。

function factory<T>(type: { new (): T }): T {
    return new type();
}

class SomeClass { }

let result = factory(SomeClass);

The result will be of type SomeClass.结果将是 SomeClass 类型。

The constructor of the class will be type checked against the interface defined in the factory method.类的构造函数将根据工厂方法中定义的接口进行类型检查。

If you want to initialize a class that takes a parameter in it's constructor you will have to specify that in the interface given to the factory method.如果你想初始化一个在它的构造函数中接受参数的类,你必须在给工厂方法的接口中指定它。

function factory<T>(type: { new (...args): T }, ...args): T {
    return new type(...args);
}


class SomeClass {
    constructor(name: string) { }
}

let a = factory(SomeClass, 'John Doe');

This has been answered here in Stack Overflow. 已在 Stack Overflow 中得到解答。 To be able to new up a new class using generics, you need to have a guarantee that the type supports instantiation.为了能够使用泛型新建一个新类,您需要保证该类型支持实例化。

You can easily support parameter less constructors using this approach but with constructors that require parameters, it would become less useful as your creatEntity method should have to accept the values for those parameters and pass to the constructor while creating the new instance and as you can see each type can have its own signature for constructor.您可以使用这种方法轻松支持无参数构造函数,但是对于需要参数的构造函数,它会变得不那么有用,因为您的 creatEntity 方法必须接受这些参数的值并在创建新实例时传递给构造函数,正如您所看到的每种类型都可以有自己的构造函数签名。

class ObjectCreator{
    static createEntity<TEntity>(type:{new():TEntity;}):TEntity{
        return new type();
    }
}

class Person{
    firstName:string;
    lastName:string;
    constructor(){
        this.firstName = "TestFirstName";
        this.lastName = "TestLastName";
    }
}

var person: Person = ObjectCreator.createEntity(Person);
alert(person.firstName);

If you want to instantiate multiple classes with different parameters in your factory function then the other answers so far are incomplete.如果您想在工厂函数中实例化具有不同参数的多个类,那么到目前为止的其他答案是不完整的。 This is what you need.这就是你所需要的。

class Hero {
  constructor(public point: [number, number]) {}
}

const entities = [];

const entityFactory = <
  T extends {
    new (...args: any[]): any;
  }
>(
  classToCreate: T,
  numberOf: number,
  ...args: ConstructorParameters<T>
): InstanceType<T>[] =>
  [...Array(numberOf)].map(() => new classToCreate(...args));

entities.push(...entityFactory(Hero, 10, [12, 10]));

console.log(entities[0].point);

We have a similar situation in which we want a generic class to take a factory function that takes parameters to instantiate the type.我们有类似的情况,我们希望泛型类采用工厂函数,该函数采用参数来实例化类型。 The provided solutions here does not cover that scenario.此处提供的解决方案不涵盖该场景。

The TypeScript approach seems to be to define Factories using an interface that expects a parameterless constructor: TypeScript 方法似乎是使用需要无参数构造函数的接口来定义工厂:

export interface QueryTypeFactory<T> {
    new () : T;
}

TypeScript will not accept a factory function that returns an instance of type T to be used in place of an actual type - even though behind the scenes constructors are just functions that returns functions that returns T. TypeScript 不会接受返回类型 T 实例的工厂函数来代替实际类型 - 即使在幕后构造函数只是返回返回 T 的函数的函数。

What we found out is that you can achieve this, but you need to an unsafe cast of the factory method like this:我们发现您可以实现此目的,但您需要像这样对工厂方法进行不安全的转换:

function dynamicFactory<T>(f: () => T): FactoryType<T> {
    return f as any as FactoryType<T>;
}

Now you can provide a factory function which can encapsulate a closure to provide dynamic behavior when instantiating objects:现在您可以提供一个工厂函数,它可以封装一个闭包以在实例化对象时提供动态行为:

function factory(argument : any) : DynamicType {
    return new DynamicType(argument);
}

And now you use the dynamic factory like this:现在你像这样使用动态工厂:

let argument = { ... };
new ThingThatCreatesEntities(dynamicFactory(() => factory(argument)));

The trick is the cast of the factory method to any and then to the required factory type.诀窍是将工厂方法强制转换为 any,然后再转换为所需的工厂类型。 It's not pretty, but it works and you can hide that in your implementation.它不是很漂亮,但它可以工作,您可以在您的实现中隐藏它。

The nice thing about this approach is that the dynamic factory can be substituted for an actual type.这种方法的好处是动态工厂可以替代实际类型。 So you could also do:所以你也可以这样做:

new ThingThatCreatesEntities(StaticType);

Where the StaticType is a class with a parameterless constructor.其中 StaticType 是具有无参数构造函数的类。

This appears to work just fine.这似乎工作得很好。

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

if the T type is interface the instance of T is look like:如果 T 类型是接口,则 T 的实例如下所示:

let a={} as T

if it is class with constructor I guess you need factor function:如果它是带有构造函数的类,我猜你需要因子函数:

factory1<T>(ctor?: NoParamConstructor<T>):T{return new ctor};
let a=this.factory<T>()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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