I am trying to build an "Entity Framework"-like ORM library for node.js and MongoDB with TypeScript.
With this library, the consumer will be able to define a Model class (ex. Person
) that will extend a Base
class, and the class decorator will add additional data to the Person class (for example, instances
array, that will contain all the Model's instances, and collection_name
that will be the MongoDB collection name for the model, etc.).
Code in TypeScript playground .
So my first step was creating a Base
class:
class Base<T>
{
static collection_name: string
static instances: Base<any>[]
_id: string
}
So the user will be able to define his model like so:
@decorator<Person>({collection_name: 'people'})
class Person extends Base<Person>
{
@field name
@field address
...
}
then I created a decorator class to set the collection_name
and instances
properties on the Person class:
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
}
}
the decorator function receives the user-generated Class
, and I am trying to create an interface that will describe the type of such class. I called it IClass
:
interface IClass<T>
{
new(): Base<T>
instances: Base<T>[];
collection_name: string
}
new
is the constructor (that returns a Base
instance) instances
and collection_name
are static properties of Base<T>
and are non-static here (I'm not sure about this, is this right?) However, when trying to define the user Model I get the following error:
@decorator<Person>({collection_name: 'people'}) // <==== ERROR HERE ===
class Person extends Base<Person>
{
}
Error:(23, 2) TS2345: Argument of type 'typeof Person' is not assignable to parameter of type 'IClass>'.
Property 'instances' is missing in type 'typeof Person' but Type 'typeof Person' is missing the following properties from type required in type 'IClass>'.
It seems like the typescript compiler is ignoring the static members inherited from Base
when checking the type of typeof Person
.
How can I define the type of the Class property of the decorator function ?
The problem as jcalz points out is that your decorator is accepting a Class
of a type that already has the static properties instances
and collection_name
. You need to use two different interfaces, one which is a type that simply constructs instances of T
with the new(): T
signature, and another that extends this interface to include the static properties your decorator will add.
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';
}
}
With this approach, all of Base
's static
members must be public. Any static
members of Base
initialized in the decorator should go in the DecoratedBaseConstructor
, and any remaining static
members not initialized in the decorator should go in the BaseConstructor
instead.
I assume that you use the generic type T
in the Base
class somehow in your actual code, but if you don't, you should remove the generic type from the Base
class and everything else will still work the same.
Check out the above snippet in this playground .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.