Объедините миксины с дженериками в node.js - PullRequest
0 голосов
/ 31 марта 2019

Я работаю над библиотекой, в которой я хотел бы поддерживать несколько разных расширений для одних и тех же библиотечных методов.
Вот мой сокращенный миксин-код, просто чтобы дать вам представление:

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());
    }
}

Моя идея заключается в реализацииможно использовать функции фреймворка (здесь просто играть) и создавать мир с настроенным создателем (он же Builder).Я попробовал дженерики, но это не компилируется.Что я делаю не так?

Ответы [ 2 ]

1 голос
/ 31 марта 2019

Вы на самом деле были недалеко, но вы размыли границы между типами и значениями.К счастью, 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 голос
/ 31 марта 2019

Кажется, что это невозможно. Поскольку 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();

Вывод кода выше:

Создание мира, содержащего:
- магия
- Завод
- собака

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...