Let's say we have some Java Node
, StringNode
and NodeFactory
classes:
public class Node<T> {
private T value;
public void init(T value){
this.value = value;
}
}
public class StringNode extends Node<String> {}
public class NodeFactory {
public static <T> Node<T> createNode(Class<? extends Node<T>> nodeClass, T value) throws Exception {
Node<T> node = nodeClass.getConstructor().newInstance();
node.init(value);
return node;
}
}
So NodeFactory.createNode(StringNode.class, "value1");
would create a StringNode
with the value: "value1"
.
How could I write such a factorymethod in typescript?
I tried to write a factorymethod in typescript:
public static createNode<T>(NodeClass: typeof Node<T>, value: T): Node<T> {
const node = new NodeClass();
node.init(value);
return node;
}
but does not accept typeof Node<T>
generic types. So I tried putting in the method's type arguments:
public static createNode<T, N extends Node<T>>(NodeClass: typeof N, value: T): Node<T> {
But the compiler does not accept that either: typeof N
: TS2693: 'N' only refers to a type, but is being used as a value here
Is there a typesafe way to write such a factorymethod in typescript?
CodePudding user response:
The generic parameter of a class applies to to instances and not the static class itself. And the value
is also for instances. So NodeClass
needs to be a "newable" type, that returns an instance with the right generic.
That means this will work:
function createNode<T>(NodeClass: { new (): MyNode<T> }, value: T): MyNode<T> {
const node = new NodeClass();
node.init(value)
return node;
}
Testing:
// good
const a = createNode(StringNode, "asd")
const b = createNode(NumberNode, 123)
const c = createNode(FunctionNode, () => 'hello world')
const d = createNode(MyNode, true) // MyNode<boolean>
// error, as expected
const bad = createNode(StringNode, 123)
createNode(MyNode, true)
is a nice bonus. Typescript picks up T
from the second argument, and puts it in the NodeClass
for you.
CodePudding user response:
I found a solution:
public static createNode<N extends { new(): Node<T> }, T>(NodeClass: N, value: T): Node<T> {
apparently I just needed N to implement a interface with a new(): Node<T>
method (or constructor).