Vyhledávání na webu

node.js - TypeScript: Dekorování odvozené třídy s typovanou dekorátorovou funkcí


Snažím se vytvořit knihovnu ORM podobnou „Entity Framework“ pro node.js a MongoDB s TypeScriptem.

S touto knihovnou bude moci uživatel definovat třídu Model (např. Person), která bude dědit ze třídy Base, a dekorátor třídy přidá další data do třídy Person (například pole instances, které bude obsahovat všechny instance Modelu, a collection_name, které bude název kolekce MongoDB pro model, atd.).

Kód v TypeScript hřišti.

Takže můj první krok byl vytvořit třídu Base:

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

Takže uživatel bude moci definovat svůj model takto:

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

poté jsem vytvořil dekorátorovou třídu k nastavení vlastností collection_name a instances ve třídě 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
    }
}

dekorátorová funkce přijímá uživatelem vytvořenou Class a já se snažím vytvořit rozhraní, které bude popisovat typ takové třídy. Nazval jsem ho IClass:

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

    instances: Base<T>[];
    collection_name: string
}
  • new je konstruktor (který vrací instanci Base)
  • instances a collection_name jsou statické vlastnosti Base<T> a zde jsou nestatické (nejsem si tím jistý, je to správně?)

Nicméně při pokusu definovat uživatelský model dostávám následující chybu:

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

Chyba:(23, 2) TS2345: Argument typu 'typeof Person' nelze přiřadit k parametru typu 'IClass>'.

Vlastnost 'instances' chybí v typu 'typeof Person', ale typ 'typeof Person' postrádá následující vlastnosti z typu požadovaného v typu 'IClass>'.

Zdá se, že překladač TypeScriptu ignoruje statické členy zděděné z Base, když kontroluje typ typeof Person.

Jak mohu definovat typ vlastnosti třídy dekorátorové funkce?

Odpověď č. 1

Problém, jak poukazuje jcalz, je v tom, že váš dekorátor přijímá Class typu, který už má statické vlastnosti instances a collection_name. Musíte použít dva různé rozhraní: jedno, které je typem, jež pouze vytváří instance T s podpisem new(): T, a druhé, které toto rozhraní rozšiřuje o statické vlastnosti, které váš dekorátor přidá.

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';
    }
}

S tímto přístupem musí být všechny static členy Base veřejné. Jakékoli static členy Base inicializované v dekorátoru by měly být umístěny do DecoratedBaseConstructor a všechny zbylé static členy, které nejsou inicializovány v dekorátoru, by měly být místo toho umístěny do BaseConstructor.

Předpokládám, že ve své skutečné kódové bázi nějak používáte generický typ T ve třídě Base, ale pokud ne, měli byste generický typ ze třídy Base odstranit a všechno ostatní bude stále fungovat stejně.

Podívejte se na výše uvedený úryvek v této herní ploše.