在 node.js 中结合 mixin 和泛型
Combine mixins with generics in node.js
我正在开发一个库,我想在其中支持相同库方法的一些不同扩展。
这是我的简短混合代码,只是为了给你一个想法:
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();
我现在的问题是如何创建一个 class 并从上面获取 "MyWorld"?
abstract class Playground {
abstract createWorld(creator: DefaultCreator);
play() {
this.createWorld(new DefaultCreator());
}
}
我的想法是实现可以使用框架功能(这里只是玩)并使用自定义的 Creator(又名 Builder)创建世界。我尝试了泛型,但无法编译。我做错了什么?
看来这不可能。由于 MyWorld
不是一个类型,它更像是一个奇怪的类型描述。
当我让我的想法指定类型时,我得到了这一行:
const MyWorld: { new(): ({ world: string[]; createDog(): void } & any); prototype: { world: string[]; createDog(): void } } = AnimalCreator(PlantCreator(DefaultCreator));
我的解决方案是强制用户按照惯例由用户和 return 我的示例的 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();
以上代码的输出:
Creating world containing:
- magic
- Plant
- Dog
你其实离得不远,但你模糊了类型和值之间的界限。幸运的是,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
可以而且只会引用任何 class 的实际构造函数,但永远不会匹配整个 class/class 对象。永远记住 classes/functions 是可执行对象。
我正在开发一个库,我想在其中支持相同库方法的一些不同扩展。
这是我的简短混合代码,只是为了给你一个想法:
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();
我现在的问题是如何创建一个 class 并从上面获取 "MyWorld"?
abstract class Playground {
abstract createWorld(creator: DefaultCreator);
play() {
this.createWorld(new DefaultCreator());
}
}
我的想法是实现可以使用框架功能(这里只是玩)并使用自定义的 Creator(又名 Builder)创建世界。我尝试了泛型,但无法编译。我做错了什么?
看来这不可能。由于 MyWorld
不是一个类型,它更像是一个奇怪的类型描述。
当我让我的想法指定类型时,我得到了这一行:
const MyWorld: { new(): ({ world: string[]; createDog(): void } & any); prototype: { world: string[]; createDog(): void } } = AnimalCreator(PlantCreator(DefaultCreator));
我的解决方案是强制用户按照惯例由用户和 return 我的示例的 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();
以上代码的输出:
Creating world containing:
- magic
- Plant
- Dog
你其实离得不远,但你模糊了类型和值之间的界限。幸运的是,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
可以而且只会引用任何 class 的实际构造函数,但永远不会匹配整个 class/class 对象。永远记住 classes/functions 是可执行对象。