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
}
newje konstruktor (který vrací instanciBase)instancesacollection_namejsou statické vlastnostiBase<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.
