TypeScript: украшение производного класса с типизированной функцией декоратора - PullRequest
0 голосов
/ 01 марта 2019

Я пытаюсь создать библиотеку ORM, похожую на Entity Framework, для node.js и MongoDB с помощью TypeScript.

С помощью этой библиотеки потребитель сможет определить класс Model (например, Person), который будет расширять класс Base, а декоратор класса добавит дополнительные данные в класс Person (дляпример, массив instances, который будет содержать все экземпляры модели, и collection_name, который будет именем коллекции MongoDB для модели и т. д.).

код в площадка TypeScript .


Итак, моим первым шагом было создание класса Base:

class Base<T>
{
    static collection_name: string
    static instances: Base<any>[]
    _id: string
}

Чтобы пользователь мог определить свою модель следующим образом:

@decorator<Person>({collection_name: 'people'})
class Person extends Base<Person>
{
    @field name
    @field address
    ...
}

затем я создал класс декоратора для установки свойств collection_name и instances класса Person:

function decorator<T>(config: { collection_name: string }) {
    return function <T extends Base<T>>(Class: IClass<T>) {
        Class.collection_name = config.collection_name;
        Class.instances = [];
        return Class
    }
}

функция декоратора получает сгенерированный пользователем Class, и я пытаюсьсоздать интерфейс, который будет описывать тип такого класса.Я назвал его IClass:

interface IClass<T>
{
    new(): Base<T>

    instances: Base<T>[];
    collection_name: string
}
  • new - это конструктор (который возвращает Base экземпляр)
  • instances и collection_name - статические свойстваиз Base<T> и здесь не являются статичными (я не уверен в этом, верно?)

Однако при попытке определить пользовательскую модель я получаю следующую ошибку:

@decorator<Person>({collection_name: 'people'})   // <==== ERROR HERE ===
class Person extends Base<Person>
{
}

Ошибка: (23, 2) TS2345: Аргумент типа 'typeof Person' не может быть назначен параметру типа 'IClass>'.

Свойство 'instances' отсутствуетв типе 'typeof Person', но в типе 'typeof Person' отсутствуют следующие свойства из типа, требуемого для типа 'IClass>'.

Кажется, что компилятор машинописного текста игнорирует статические члены, унаследованные от Base при проверке типа typeof Person.

Как определить тип свойства Class функции decorator?

1 Ответ

0 голосов
/ 01 марта 2019

Проблема , как указывает jcalz , заключается в том, что ваш декоратор принимает Class типа, который уже имеет статические свойства instances и collection_name.Вам нужно использовать два разных интерфейса, один из которых является типом, который просто создает экземпляры T с сигнатурой new(): T, а другой расширяет этот интерфейс для включения статических свойств, которые добавит ваш декоратор.

class Base<T> {
    static _id = 0;
    private _id: number;

    constructor () {
        this._id = Base._id++;
    }
}

interface BaseConstructor<T extends Base<T>> {
    _id: number;
    new(): T;
}

interface DecoratedBaseConstructor<T extends Base<T>> extends BaseConstructor<T> {
    instances: T[];
    collection_name: string;
}

function decorator<T extends Base<T>>(config: { collection_name: string }) {
    return (baseConstructor: BaseConstructor<T>): DecoratedBaseConstructor<T> => {
        const decoratedBaseConstructor = baseConstructor as Partial<DecoratedBaseConstructor<T>>;
        decoratedBaseConstructor.collection_name = config.collection_name;
        decoratedBaseConstructor.instances = [];
        return decoratedBaseConstructor as DecoratedBaseConstructor<T>;
    };
}

@decorator<Person>({collection_name: 'people'})
class Person extends Base<Person> {
    name: string;

    constructor () {
        super();
        this.name = 'foo';
    }
}

При таком подходе все члены Base static должны быть открытыми.Любые static члены Base, инициализированные в декораторе, должны идти в DecoratedBaseConstructor, а все оставшиеся static члены, не инициализированные в декораторе, должны идти в BaseConstructor.

Я полагаючто вы используете универсальный тип T в классе Base каким-то образом в своем фактическом коде, но если вы этого не сделаете, вам следует удалить универсальный тип из класса Base, и все остальное будет работать так же.

Посмотрите приведенный выше фрагмент на этой игровой площадке .

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