Javascript 通过泛型函数动态修改TypeScript类

Javascript 通过泛型函数动态修改TypeScript类,javascript,class,typescript,factory,Javascript,Class,Typescript,Factory,如何通过泛型函数在类上创建静态方法 假设您想在一组类上添加一个函数make,这取决于该类的静态shouldMake是true还是false。这些make函数应该是工厂,创建类的实例 以下JavaScript可以工作: function makeMaker( cls ) { if ( cls.shouldMake ) cls.make = function( ...args ) { return new cls( ...args ); }; return cls;

如何通过泛型函数在类上创建静态方法

假设您想在一组类上添加一个函数
make
,这取决于该类的静态
shouldMake
是true还是false。这些
make
函数应该是工厂,创建类的实例

以下JavaScript可以工作:

function makeMaker( cls )
{
    if ( cls.shouldMake )
        cls.make = function( ...args ) { return new cls( ...args ); };
    return cls;
}
您可以通过以下方式运行一组类:

outClasses = inClasses.map( makeMaker );
我希望这样的东西在TypeScript中工作:

function makeMaker< T >( cls: T ): T
{
    if ( cls.shouldMake )
        cls.make = function( ...args: any[] ) { return new cls( ...args ); }
    return cls;
}
原因:

类型“new(…args:any[])=>T”上不存在属性“shouldMake”

类型“new(…args:any[])=>T”上不存在属性“make”

这是有道理的,尽管即使从未调用
makeMaker
,也会发生此错误。因此,我们需要确保T的构造函数(T类本身)是一种类型,其中
shouldMake
make
属性是有效的


你会怎么做?基本上,如何让类类型从接口继承,我们可以在接口中约束它,或者,如何描述类上的有效静态属性?根据:这似乎不受支持,因此,依赖于此逻辑的大型JavaScript代码库是否不可能转换为TypeScript(即,排除或多或少的完全重写)?

这听起来像是一个用于方法和类型转换的用例。在讨论实际解决方案之前,我将介绍必要的序言。首先,我们可以声明一个接口,该接口为可以成为Maker的所有类类型实现(使用
T
类型的对象构造函数):

现在我们可以实现
makeMaker
。如果我们将类对象强制转换为
C&Maker
,则
make
可以存在,并且可以正常设置它。该方法原型的重要部分是确保
C
具有构造函数和
shouldMake
字段,并且返回类型保留有关
C
的所有信息,以及
Maker
中的额外方法

function makeMaker<T, C extends Class<T>>( cls: C ): C & Maker<T>
{
    if (cls.shouldMake)
        (<C & Maker<T>>cls).make = function( ...args: any[] ) { return new cls( ...args ); };
    return cls;
}

下面是一个用法示例。请注意,您应该始终检查
make
是否可用,因为编译器无法为您验证这一点

class Foo {
    private name?: string;

    static shouldMake = true;

    new(name?: string) {
        this.name = name;
    }

    hasName(): boolean {
        return typeof this.name === 'string' && this.name !== '';
    } 
}

const FooMaker = makeMaker<Foo, typeof Foo>(Foo);
if (FooMaker.shouldMake) {
    let unnamedFoo = FooMaker.make();
    console.log(unnamedFoo.hasName()); // false
} else {
    // unreachable in this case
}
class-Foo{
私有名称?:字符串;
静态shouldMake=true;
新建(名称?:字符串){
this.name=名称;
}
hasName():布尔值{
返回this.name的类型=='string'&&this.name!=='';
} 
}
const FooMaker=makeMaker(Foo);
如果(FooMaker.shouldMake){
让unnamefoo=FooMaker.make();
console.log(unnamefoo.hasName());//false
}否则{
//在这种情况下是无法到达的
}
interface Maker<T> {
    shouldMake: boolean;
    make?(...args: any[]): T;
}
function makeMaker<T, C extends Class<T>>( cls: C ): C & Maker<T>
{
    if (cls.shouldMake)
        (<C & Maker<T>>cls).make = function( ...args: any[] ) { return new cls( ...args ); };
    return cls;
}
function makeMaker(cls) {
    if (cls.shouldMake)
        cls.make = function (...args) { return new cls(...args); };
    return cls;
}
class Foo {
    private name?: string;

    static shouldMake = true;

    new(name?: string) {
        this.name = name;
    }

    hasName(): boolean {
        return typeof this.name === 'string' && this.name !== '';
    } 
}

const FooMaker = makeMaker<Foo, typeof Foo>(Foo);
if (FooMaker.shouldMake) {
    let unnamedFoo = FooMaker.make();
    console.log(unnamedFoo.hasName()); // false
} else {
    // unreachable in this case
}