Typescript: Override static factory method of parent Class in Child method - node.js

I'm running into some problems with dependency injection with Typescript. On every Class that I add a factory static method where all dependencies are set. I do this for testing purposes so that I'm still able to use the TDD approach.
Now I'm running into some problems with overriding the factory method of the parent class in a child class. Example:
interface DepsA {
a: string
}
interface DepsB extends DepsA {
b: Child1
}
class Parent {
constructor(protected deps: DepsA | DepsB) {}
public static factory<T extends Parent>() {
return new this({a: 'This is a nice Dependency'}) as T
}
}
class Child1 extends Parent {}
class Child2 extends Parent {
public static factory() {
return new this({a: 'This is a nice Dependency', b: Child1.factory()})
}
}
const child1 = Child1.factory<Child1>()
const child2 = Child2.factory()
The error what I receive is:
[ts]
Class static side 'typeof Child2' incorrectly extends base class static side 'typeof Parent'.
Types of property 'factory' are incompatible.
Type '() => Child2' is not assignable to type '<T extends Parent>() => T'.
Type 'Child2' is not assignable to type 'T'.
I know why I get the error, but have at this point no idea anymore how to fix it, otherwise than renaming the factory static method in Child2.
UPDATE: A Related bug report to this problem, that explains automatically why I use a Generic on the factory method is: #26298

First, there's a pre-defined conditional type called InstanceType which could help you to infer the class type from the static member:
public static factory<T extends typeof Parent>(this: T) {
return new this({ a: 'This is a nice Dependency' }) as InstanceType<T>
}
Second, if you override a method, static or not, in a child class, it should have a compatible signature, including the generic stuff.
Consequently, your code block could look like this (see in Typescript Playground):
interface DepsA {
a: string
}
interface DepsB extends DepsA {
b: Child1
}
class Parent {
constructor(public deps: DepsA | DepsB) {}
public static factory<T extends typeof Parent>(this: T) {
return new this({ a: 'This is a nice Dependency' }) as InstanceType<T>
}
}
class Child1 extends Parent {}
class Child2 extends Parent {
public static factory<T extends typeof Parent>(this: T) {
return new this({a: 'This is a nice Dependency', b: Child1.factory()}) as InstanceType<T>
}
}
const child1 = Child1.factory() // Type: Child1
const child2 = Child2.factory() // Type: Child2
From there, returning the proper deps type, rather than an union, would also be possible in non static members, using as this["deps"]. But you'd have to revamp a bit your code.
Hope it helps ;-)

Related

Is there a way of getting the names of all subclasses

I want to get the names of all subclasses of a base class. From that I want to #:build an enum where every entry is the name of a class. Is this possible?
Here is the code. The order of macro executions is undefined, as specified in documentation.
[AutoBuildingMacro.hx]
import haxe.macro.Context;
import haxe.macro.Expr;
final mapofnames = new Map<String,Array<String>>();
class AutoBuildingMacro {
public static macro function fromBaseClass(base):Array<Field> {
var parent = haxe.macro.ExprTools.toString(base);
var names = mapofnames[parent];
if ( names == null ) {
names = new Array<String>();
mapofnames[parent] = names;
}
names.push(Context.getLocalClass().toString());
trace(mapofnames);
return null;
}
}
[Main.hx]
import AutoBuildingMacro;
class Main {
static public function main() {}
}
#:autoBuild(AutoBuildingMacro.fromBaseClass(Parent))
class Parent {
}
class Child1 extends Parent {
}
class Child2 extends Parent {
}
class Child3 extends Child2 {
}
Output:
AutoBuildingMacro.hx:15: {Parent => [Child2]}
AutoBuildingMacro.hx:15: {Parent => [Child2,Child3]}
AutoBuildingMacro.hx:15: {Parent => [Child2,Child3,Child1]}
You are looking for auto-build macro feature https://haxe.org/manual/macro-auto-build.html
And then enum building https://haxe.org/manual/macro-enum-building.html
I wrote a library called compiletime that has this feature:
// Returns a list of all the classes that inherit MySuperClass, no matter what package
CompileTime.getAllClasses(MySuperClass);
The approach I took worked around the build order problem by using the Context.onGenerate hook to build an array of matching classes at the end of compilation (rather than as each class is compiled) and add the array to “metadata”, which can still be modified in the “onGenerate” step. I then use this metadata in a separate runtime class to get the array of classes at runtime.

TypeScript: Decorating a derived class with typed decorator function

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.

Implicit inheritance

I'm using a library that extends the prototype of my classes. Although not important, in my case the library adds the EventEmitter prototype to my class, then it instantiates the class with an instance of an EventEmitter. I need to register for the events, but I'm having trouble getting this to play nice. The library was created before ES6 style inheritance via the extends keyword was introduced and I cannot change the library, thus I must rely on some kind of implicit inheritance in my classes.
Is it possible to implicitly inherit another type?
Example code
import { EventEmitter } from 'events';
import { inherits } from 'util';
class Foo {
constructor() {
// Do something assuming
// 'this' is an event emitter
}
}
inherits(Foo, EventEmitter);
Foo.call(new EventEmitter());
What I've tried
Explicit inheritance - expects call to super, the object is already an event emitter, I just want the types tied to my class.
class Foo extends EventEmitter {
constructor() {
this.on('event', ...)
}
}
[ts] Constructors for derived classes must contain a 'super' call.
Casting to EventEmitter before calling those methods.
class Foo {
constructor() {
const e = this as EventEmitter;
e.on('event', ...)
}
}
[ts]
Type 'this' cannot be converted to type 'EventEmitter'.
Type 'Foo' is not comparable to type 'EventEmitter'.
Property 'addListener' is missing in type 'Foo'.
Casting to any then to EventEmitter works, but it feels like a hack. Requiring a cast everywhere is also not very elegant, I feel there is a more appropriate solution.
class Foo {
constructor() {
const e = this as any as EventEmitter;
e.on('event', ...)
}
}
You can use class/interface declaration merging :
declare class EventEmitter{
on(n:string):void
}
interface Foo extends EventEmitter { }
class Foo {
constructor() {
this.on('event')
}
}

Variant Generic Interfaces

I have a generic interface, and a class implementing that interface with a concrete type parameter. I also have a generic class using the generic interface as its type constraint, but the type parameter is restricted to be a subclass of a certain base class. I want to instance the generic class with the class implementing that interface but have a problem of converting the class to that interface. The following code illustrates all the classes I mentioned:
The base class:
class DomainBase
{
}
The class used as the type parameter in the interface
class Person : DomainBase
{
}
The generic interface:
public interface IRepository<T> where T : class
{
IEnumerable<T> Fetch();
T Persist(T item);
}
The class implementing the generic interface:
class PersonRepository : IRepository<Person>
{
public IEnumerable<Person> Fetch()
{
...
}
public Person Persist(Person item)
{
...
}
}
The generic class using the generic interface:
class DomainBaseViewModel<Repository>
where Repository : IRepository<DomainBase>, new()
{
private Repository repository = new Repository();
private ObservableCollection<DomainBase> items;
}
However, the following line can't get compiled because PersonRepository is unable to be converted to IRepository<DomainBase>:
var viewModel = new DomainBaseViewModel<PersonRepository>();
Although I can solve this issue by covariance but it disallows the use of the type parameter in parameter lists:
public interface IRepository<out T> where T : class
{
...
T Persist(object item);
}
class PersonRepository : IRepository<Person>
{
public Person Persist(object item)
{
...
}
}
So I have to convert the parameter to Person, which compromises type safety.
Is there a better way to allow covariance and the use of type parameter in parameter lists in this case?
No - the whole point of the restriction on covariance is that it guarantees safety. A PersonRepository isn't an IRepository<DomainBase> because you can't ask it to persist any arbitrary DomainBase object. What would you expect this code to do?
class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());
PersonRepository doesn't know how to persist Product values.
If in some cases you only need the "read" parts of the repository interface, you could always call that out explicitly:
public interface IRepositoryReader<out T>
{
IEnumerable<T> Fetch();
}
public interface IRepository<T> : IRepositoryReader<T>
{
T Persist(T item);
}
Then your DomainBaseViewModel class could be:
class DomainBaseViewModel<TRepository>
where TRepository : IRepositoryReader<DomainBase>, new()
{
private TRepository repository = new TRepository();
private ObservableCollection<DomainBase> items;
}
That doesn't work if you want your DomainBaseViewModel to persist items as well though. In that case, perhaps it should be generic in the type of model as well:
class DomainBaseViewModel<TRepository, TEntity>
where TRepository : IRepository<TEntity>, new()
{
private TRepository repository = new Repository();
private ObservableCollection<TEntity> items;
}
Then:
var viewModel = new DomainBaseViewModel<PersonRepository, Person>();

Retrieving an Enum through a class and its descendants

I have a class that I've defined, and I have a number of child classes derived from it. The parent class has an enum (let's call it 'Barf'). Each descendant ALSO has an enum with the same name but not the same values. What I'm trying to figure out how to do is write a method in the ancestor class that gets the version of Barf for the actual class of the instantiated object. So if I create an instance of Ancestor, I'd like to have this method process the entries for Ancestor.Barf . If I create an instance of one of the child classes of Ancestor, I'd like to have the method process Childx.Barf values.
Obviously this is going to be a Reflection solution, but my reflection skills are pretty sparse. Any help?
Just for the fun of it, here is a possible approach:
public class Ancestor {
public enum Caffeine {
Tea,
Coffee
}
public void ProcessValues() {
var type = GetType();
var nestedEnums = from t in type.GetNestedTypes()
where t.IsEnum
select t;
var nestedEnum = nestedEnums.Single();
foreach(var val in Enum.GetValues(nestedEnum)) {
Console.WriteLine("Drinking {0}", val);
}
}
}
public class Descendant : Ancestor {
public new enum Caffeine {
Jolt,
RedBull
}
}
// The following prints:
// Drinking Jolt
// Drinking RedBull
Ancestor x = new Descendant();
x.ProcessValues();
Of course, you could achieve the same thing using polymorphism:
public class Ancestor {
public enum Caffeine {
Tea,
Coffee
}
protected virtual Type GetNestedEnum() {
return typeof(Ancestor.Caffeine);
}
public void ProcessValues() {
var nestedEnum = GetNestedEnum();
foreach(var val in Enum.GetValues(nestedEnum)) {
Console.WriteLine("Drinking {0}", val);
}
}
}
public class Descendant : Ancestor {
public new enum Caffeine {
Jolt,
RedBull
}
protected override Type GetNestedEnum() {
return typeof(Descendant.Caffeine);
}
}
As Justin Morgan has pointed out however, having the need for such a construct may be an indication of an underlying design issue in your code.

Resources