将mixin与node.js中的泛型相结合

问题描述 投票:0回答:2

我正在开发一个库,我想支持相同库方法的一些不同扩展。 这是我的简短mixins代码,只是为了给你一个想法:

type Creator<T = {}> = new (...args: any[]) => T;
class DefaultCreator {
        world:string[] = [];
        someMagic() {
            this.world.push("magic")
        }
        getWorldList() {
            return this.world;
        }
}

function PlantCreator<TBase extends Creator>(Base: TBase) {
    return class extends Base {
        private world = (<DefaultCreator><any>this).world;
        createPlant() {
            this.world.push("Plant")
        }
    };
}

function AnimalCreator<TBase extends Creator>(Base: TBase) {
    return class extends Base {
        private world = (<DefaultCreator><any>this).world;
        createDog() {
            this.world.push("Dog")
        }
    };
}

我这样使用它:

const MyWorld = AnimalCreator(PlantCreator(DefaultCreator));
const world = new MyWorld();
world.someMagic();
world.createPlant();
world.createDog();

我现在的问题是如何创建一个带有上面“MyWorld”的课程?

abstract class Playground {
    abstract createWorld(creator: DefaultCreator);
    play() {
        this.createWorld(new DefaultCreator());
    }
}

我的想法是实现可以使用框架功能(这里只是播放)并使用自定义的Creator(aka Builder)创建世界。我尝试了泛型,但是没有编译。我究竟做错了什么?

typescript generics mixins
2个回答
1
投票

你实际上并不遥远,但你模糊了类型和价值观之间的界限。幸运的是,Javascript / Typescript可以让你这样做。

// Match only a constructor
type Creator<T = {}> = new (...args: any[]) => T;

// Match an actual class
interface CreatorClass<T> {
    new(...args: any[]): T;
}

class DefaultCreator {
        world:string[] = [];
        someMagic() {
            this.world.push("magic")
        }
        getWorldList() {
            return this.world;
        }
}

function PlantCreator<TBase extends Creator>(Base: TBase) {
    return class extends Base {
        private world = (<DefaultCreator><any>this).world;
        createPlant() {
            this.world.push("Plant")
        }
    };
}

function AnimalCreator<TBase extends Creator>(Base: TBase) {
    return class extends Base {
        private world = (<DefaultCreator><any>this).world;
        createDog() {
            this.world.push("Dog")
        }
    };
}

interface IPlantWorld {
    createPlant(): void;
}

interface IAnimalWorld {
  createDog();
}

const MyWorld: CreatorClass<IPlantWorld & IAnimalWorld> = AnimalCreator(PlantCreator(DefaultCreator));

abstract class Playground {
    // I want to have a reference of the class' constructor
    createOtherWorld<T>(creator: Creator<T>) {
        return new creator();
    }

    // I want to reference the class itself
    createWorld<T>(creator: CreatorClass<T>): T {
        return new creator() as T;
    }
    play() {
        this.createWorld(DefaultCreator);
    }
}



class EverythingPlaygroundFactory extends Playground {
    play() {
        // provide the type information
        return this.createWorld<IAnimalWorld & IPlantWorld>(MyWorld);
    }
}

let pg = new EverythingPlaygroundFactory();
let world = pg.createWorld(MyWorld);
world.createPlant();
world.createDog();

pg.createOtherWorld(MyWorld.prototype.constructor);

这可能更符合您的要求。

需要注意的事项:

type Creator<T = {}> = new (... args: any[]) => T可以并且只会引用任何类的实际构造函数,但永远不会匹配整个类/类对象。永远记住类/函数是可执行对象。


1
投票

看来这是不可能的。由于MyWorld不是一种类型,因此它更像是一种奇怪的类型描述。

当我让我的想法指定类型时,我得到这一行:

const MyWorld: { new(): ({ world: string[]; createDog(): void } & any); prototype: { world: string[]; createDog(): void } } = AnimalCreator(PlantCreator(DefaultCreator));

我的解决方案是按照惯例强制用户按用户创建实例并返回我的示例的world

我修改了我的游乐场,以便我得到这个代码:

abstract class Playground {
    abstract createWorld(): string[];
    play() {
        console.log("Creating world containing:");
        this.createWorld().forEach(item => console.log(`- ${item}`))
    }
}

class MyPlayground extends Playground {
    createWorld(): string[] {
        const world = new MyWorld();
        world.someMagic();
        world.createPlant();
        world.createDog();
        return world.getWorldList();
    }
}

new MyPlayground().play();

上面代码的输出:

创建包含以下内容 - 魔法 - 植物 - 狗

© www.soinside.com 2019 - 2024. All rights reserved.