Hi,
I understand that typescript support in node red is a bit sketchy, but in some tests I have gotten it to work really well. I find it really clean, and it lets me test and use these classes in other places. My ultimate goal is to make the logic fairly unaware of node red, except for a little glue needed to create objects in the NR universe.
What I'm trying to do is clean up some of the object instantiation around this. I started with what was done here and here and both of those together gave me a great start.
Unfortunately, I'm doing something wrong, because I get this:
10 Feb 15:31:28 - [error] [gis-powermon-ll:15fee740e28a2bda] TypeError: this.updateWires is not a function
This suggests that createNode() is not actually attaching Node to the instance as I expect it should. Let me explain the code. First, there is an (empty) base class. Its only real purpose is so that when createNodes() calls the javascript superclass that the typescript compiler believes that extension (or sub-classing, whatever you want to call it) results in a fully typed object. I really think of it more as a mix-in. So the base class looks like this:
import { Node as NodeRedNode, NodeDef, NodeAPI } from "node-red";
export abstract class NRTSNode {}
export interface NRTSNode extends NodeRedNode {}
For those not familiar, declaring an abstract class and then an interface with the exact same name is a form of interface merging, a feature of typescript. What it does here is lets me refer to objects in NodeRedNode (an alias I made for a NodeRed "Node").
Next, I created my actual class:
class PowermonLinkLayerNode extends NRTSNode {
private waiting_for_flag: boolean = true;
private escaped: boolean = false;
private buffer: Array<number> = [];
constructor(RED: NodeAPI, config: GisPowermonLLNodeDef) {
super();
RED.nodes.createNode(this, config);
}
...
The call to RED.nodes.createNode() is explained in the documentation very well:
// call createNode()
// Called from a Node's constructor function,
// invokes the super-class constructor and attaches
// any credentials to the node.
// Params:
// node – the node object being created
// def – the instance definition for the node
So, in conjunction with the interface merging, I should now have my own class that is overlayed with the Node interface for typing purposes, and the call to createNode should attach all the missing pieces of "Node" the base class. This is as I understand a fairly common way to have a Typescript class that extends a javascript class. So far so good.
Finally, to create a node, I need to register it as usual. I want to have the actual logic implemented by the TypeScript class, so:
export = (RED: NodeAPI): void => {
function createMyVerySpecialNode(config: GisPowermonLLNodeDef) {
let instance = new PowermonLinkLayerNode(RED, config);
instance.on("input", (msg: any, send, done) => {
instance.onInput(msg)
})
}
RED.nodes.registerType("gis-powermon-ll", createMyVerySpecialNode);
}
However, when I actually try to run this on a flow, I get this.updateWires is not a function
and I'm not really sure why. My interpretation of the documentation is the function exported by the ts file registers the new type with (name, static_constructor_function)
and that is what happens - when I place a node on a flow this inner function gets called; if I place two of the same nodes it gets called twice. However it seems to fail out. It fails in my constructor, either in createNode()
or immediately after, I'm not sure.
I'd prefer people not reply trying to talk me out of using Typescript. This is as much an intellectual exercise as a useful one. I appreciate that it might be useful to me and just more complication to others. But if anybody out there has some thoughts on how I can proceed to debug this please reply. I feel like this is very close to working.
[EDIT] THIS CODE is what I think is the right .createNode()
and I'm not sure what this:
function createNode(node,def) {
Node.call(node,def);
which looks like it's calling this guy that ends in:
this.updateWires(n.wires);
so that's definitely where the problem is coming from. I think that my class is not really a Node (even though it's declared as such). I must be missing a step.
--Chris