how to typecast T extends Class in typescript - node.js

I want to avoid using any for clazz. Casting it to A doesn't allow me to create a new instance as it assumes it's an abstract class. What would be the correct type to cast clazz to?
abstract class A {}
class B extends A {}
class C extends A {}
class D extends A {}
const clazz: any = getOneOfBCDRandom();
const c = new clazz();
getOneOfBCDRandom<T extends A>(): T {
return [B, C, D][Math.floor(Math.random() * 3)]
}
i removed the private state from the getOneOfBCDRandom function as it was a mistake.
The solution was to not use generics but:
new () => A
so this works as expected:
abstract class A {}
class B extends A {}
class C extends A {}
class D extends A {}
const clazz: new () => A = getOneOfBCDRandom();
const c = new clazz();
getOneOfBCDRandom(): new () => A {
return [B, C, D][Math.floor(Math.random() * 3)]
}
Using generics was just my first approach

Related

Typescript: Override static factory method of parent Class in Child method

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 ;-)

Check inheritance in nodejs

What is the best way to check inheritance in nodejs?
I'm trying to use instanceof in a instance of a class of another module that inherits a class for this module.
file a.js
class A{
}
class B extends A{
}
var b = new B();
b instanceof A ///this work
global.c instanceof A //this doesn't work
module.exports = A;
file c.js
var A = require("./a");
class C extends A{
}
global.c = new C();
It is because of loading issue! When you load class C, it request class A and it is run before the C is defined.
I have tried it myself, if I did it as you mentioned and requested both classes, the second one comparision failed.
However this one works:
a.js
class A{
callMeLaterAligator(){
console.log(b instanceof A) ///this work
console.log(global.c instanceof A) //this now work
}
}
class B extends A{
}
var b = new B();
module.exports = A;
c.js
var A = require("./a");
class C extends A{
}
global.c = new C();
The main method
require('services/c');
const a = require('services/a');
const aInst = new a();
aInst.callMeLaterAligator();
having output
true
true
To better understand whats going on, I have created this example
a.js
console.log('Hello, I am class A and I am not yet defined');
class A{
}
class B extends A{
}
var b = new B();
console.log('Hello, I am class A and I will compare something');
console.log(b instanceof A) ///this work
console.log(global.c instanceof A) //this doesn't work
module.exports = A;
c.js
console.log('Hello, I am class C and I am not yet defined');
var A = require("./a");
console.log('Hello, I am class C and I will now try to defined myself');
class C extends A{
}
console.log('Hello, I am class C and I am defined');
global.c = new C();
console.log('Hello, I am class C and I am in global.c');
server.js
require('services/c');
Having this output
Hello, I am class C and I am not yet defined
Hello, I am class A and I am not yet defined
Hello, I am class A and I will compare something
true
false
Hello, I am class C and I will now try to defined myself
Hello, I am class C and I am defined
Hello, I am class C and I am in global.c
If you change it to require "a" first, then the C is not loaded at all
server.js change :
require('services/a');
Having this output
Hello, I am class A and I am not yet defined
Hello, I am class A and I will compare something
true
false

How do I get the annotation value 'used'

I want to retrieve the annotation value used from MyAnnot. I am getting 3 annotations in the list even though there are only 2. Also, I have tried obtaining the used field of MyAnnot but with no success. I would like to return a map where MyAnnot's used is the key and type as the map's value.
// Then, define your class with it's annotated Fields
class MyClass {
#MyAnnot(used = "Hey", type = "There")
String fielda
#MyAnnot(used = "denn", type = "Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation(obj, annotClass) {
def op = []
def annos = []
def i = 0
obj.properties.findAll { prop ->
obj.getClass().declaredFields.find {
it.name == prop.key && annotClass in it.declaredAnnotations*.annotationType()
annos=it.declaredAnnotations
i++
if(annos)
op << annos[0] as Set
// println"Props ${annos[0]}"
}
}
op.each{ println "${it} and i is ${i}"}
}
// Then, define an instance of our class
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
// And print the results of calling our method
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
First of all you need to mark you annotation with: #Retention(RetentionPolicy.RUNTIME) to make it available for processing at runtime, so it will be:
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
Then, used and type are not fields, nor properties but methods, so they must be invoked and get.
The script will be:
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Retention
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
class MyClass {
#MyAnnot(used="Hey" ,type="There")
String fielda
#MyAnnot(used="denn", type="Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation( obj, annotClass ) {
def c = obj.getClass()
c.declaredFields.findAll { field ->
field.isAnnotationPresent(annotClass)
}.collect { found ->
def a = found.getAnnotation(annotClass)
[(a.used()): a.type()]
}.sum()
}
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
Mind that passing only a class of annotation is not sufficient, since you don't know the methods (used and type) to be invoked on the annotation. The following method will work only for MyAnnot class.

How to extend Class method and effect its subclass

for instance:
class Kls {
}
class SubKls extends Kls {
}
Kls.metaClass {
trySmth() {}
}
new SubKls().trySmth()
I hope SubKls can invoke extended-method #trySmth
Update: maybe i can use #invokeMissingMethod to solve it, but it's not enough, since I also wanna extend some Core Class such as java.lang.Object and java.lang.String
No problem with groovy 2.4.0:
class Kls {}
class SubKls extends Kls {}
Kls.metaClass {
foo = {'bar'}
}
assert new SubKls().foo() == 'bar'

Groovy: Is there a way to implement multiple inheritance while using type-checking?

#groovy.transform.TypeChecked
abstract class Entity {
...
double getMass() {
...
}
...
}
#groovy.transform.TypeChecked
abstract class Location {
...
Entity[] getContent() {
...
}
...
}
#groovy.transform.TypeChecked
abstract class Container {...} //inherits, somehow, from both Location and Entity
#groovy.transform.TypeChecked
class Main {
void main() {
double x
Container c = new Chest() //Chest extends Container
Entity e = c
x = e.mass
Location l = c
x = l.content //Programmer error, should throw compile-time error
}
}
Essentially, is there a way to achieve this, without sacrificing any of the three properties outlines in main():
Direct access to fields, even virtual fields
Assigning to both super-classes
Typechecking (at compile-time)
I don't think you can do that with classes. Maybe you'd wanted traits (under discussion update: available in Groovy 2.3 and already rocking!) or, for a pure dynamic solution, #Mixin, which you'd back up with a good test suite.
My guess: #Delegate is your best friend here, but, as it stands, you can only store a Chest object in a Container type variable. So you'd need some interfaces.
Even if the superclass is not under your control, you can use groovy as operator to make it implement an interface.
First, i rewrote your classes to remove the abstract and add interfaces:
import groovy.transform.TypeChecked as TC
interface HasMass { double mass }
interface HasContent { Entity[] getContent() }
#TC class Entity implements HasMass { double mass }
#TC class Location {
Entity[] getContent() {
[new Entity(mass: 10.0), new Entity(mass: 20.0)] as Entity[]
}
}
Note i didn't added HasContent to Location, to show the usage of as.
Second, comes the Container and Chest. #Delegate is added and it auto-inherits the interfaces of the delegates:
#TC
abstract class Container {
#Delegate Location location = new Location()
#Delegate Entity entity = new Entity()
}
#TC class Chest extends Container { }
Last, it becomes type-checkable, as long as you stick to interfaces:
#TC class Mult {
static main(args) {
def x // use 'def' for flow-typing
Container c = new Chest() //Chest extends Container
HasMass e = c
x = e.mass
def l = c as HasContent
x = l.content //Programmer error, should throw compile-time error
assert c.content.collect { Entity it -> it.mass } == [10.0, 20.0]
}
}

Resources