Env.get() is declared to return a string, but in subflows, it doesn't

Hi,
a subflow has the environment variable List set to type JSON. The value of this variable is set to:

["foo","bar"]

The variable is retrieved in a function node inside the subflow via env.get(). When I hover over the get in the integrated monaco editor, the online help shows up and tells me that this function returns a string.

But with the lines:

node.warn(typeof env.get('List'));
node.warn(Array.isArray(env.get('List')));

I get: "object" and true.

When I then try to iterate through the array by typing

for (let i in env.get('List'))

the editor marks env.get('List') with this error:
The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'string'.(2407)
That's true, if env.get() would really return a string, but it doesn't. If I ignore the warning and deploy anyway, the code works as expected.

I guess the functions return value was updated at some time, but the definition, the editor uses, was not?

node-red v3.0.2
node.js v16.19.0

Here's a sample (sub)flow:

Example flow:
[
    {
        "id": "85f46028b8243fab",
        "type": "subflow",
        "name": "Subflow 1",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 120,
                "y": 60,
                "wires": [
                    {
                        "id": "c9861e59b0f13953"
                    }
                ]
            }
        ],
        "out": [],
        "env": [
            {
                "name": "List",
                "type": "json",
                "value": ""
            }
        ],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "c9861e59b0f13953",
        "type": "function",
        "z": "85f46028b8243fab",
        "name": "function 1",
        "func": "let List = env.get('List');\nnode.warn(typeof List);\nnode.warn(Array.isArray(List));\nnode.warn(List);\nfor (let i in List)\n    node.warn(List[i]);\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 240,
        "y": 60,
        "wires": [
            []
        ]
    },
    {
        "id": "3c7ce0954fc376d7",
        "type": "subflow:85f46028b8243fab",
        "z": "d49e15e1b5f5352d",
        "name": "",
        "env": [
            {
                "name": "List",
                "value": "[\"foo\",\"bar\"]",
                "type": "json"
            }
        ],
        "x": 620,
        "y": 180,
        "wires": []
    },
    {
        "id": "da28e302f3f3b142",
        "type": "inject",
        "z": "d49e15e1b5f5352d",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 460,
        "y": 180,
        "wires": [
            [
                "3c7ce0954fc376d7"
            ]
        ]
    }
]

for in is for objects an will return the keys of the object. For an array use for of

An array is an object. I use for...in if I need the index inside the loop and for...of otherwise.

Btw: otherwise than stated in the error message, for...in is indeed able to iterate over a string:

let foo = 'bar';
for (let i in foo)
	node.warn('foo[' + i + '] = ' + foo[i]);

Output:

"foo[0] = b"
"foo[1] = a"
"foo[2] = r"

The editor will show up with the same error. I think this is another issue (perhaps related to monaco?).

Anyway, the point of this posting is, that the env.get() is declared to always return a string which isn't correct.
I appreciate that env.get() returns the actual type instead of casting everything to a string, but the editor should know about that fact.

technically a javascript array is an object, but most make the distinction of object(dictionary) as object and object(array) as an array.

Not sure what your subflow env var is returned as, will take a look if you export an example flow.

If you want the env var to return a string set it as a string not json

An example flow is already added at the end of the original post.

No, I want env.get() to return JSON. The problem is that the editor thinks that the function returns a string.

I don't download text docs to my computer.

To share a flow use the </> button and paste it between tha backticks, Then all anyone needs to do is hit the copy icon, no download or extra clicks or having to delete a file.
e.g.

[{"id":"01cd665373cb4cef","type":"subflow","name":"Subflow 1","info":"","category":"","in":[{"x":40,"y":80,"wires":[{"id":"fefb4c0d6c852023"}]}],"out":[{"x":340,"y":80,"wires":[{"id":"fefb4c0d6c852023","port":0}]}],"env":[{"name":"list_string","type":"str","value":"{\"test\":\"tester\"}","ui":{"label":{"en-US":"Enter Topic Suffix"},"type":"input","opts":{"types":["str"]}}},{"name":"list_array","type":"json","value":"{\"test\":\"tester\"}"}],"meta":{},"color":"#DDAA99"},{"id":"fefb4c0d6c852023","type":"function","z":"01cd665373cb4cef","name":"function 12","func":"const list_string = env.get(\"list_string\");\nconst list_array = env.get(\"list_array\") ;\nmsg.payload = { \"string\": list_string, \"array\": list_array};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":190,"y":80,"wires":[[]]},{"id":"938f0bc5c5f1f3c5","type":"inject","z":"da8a6ef0b3c9a5c8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":4720,"wires":[["d093ee9769e932b6"]]},{"id":"d093ee9769e932b6","type":"subflow:01cd665373cb4cef","z":"da8a6ef0b3c9a5c8","name":"","x":260,"y":4720,"wires":[["72fd6ce6c8e5692e"]]},{"id":"72fd6ce6c8e5692e","type":"debug","z":"da8a6ef0b3c9a5c8","name":"debug 231","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":530,"y":4620,"wires":[]}]

You can tell monaco to ignore the error add // @ts-ignore before the env line.

I did exactly that. No download is attached to the first post. Simply expand the "Example flow:" section and copy the text.

To clarify the intention of my post: This issue is not about the actual code. There are several workarounds I can use. I just wanted to point out a weird behaviour in node-red that should be examined and fixed, if considered a bug.

I wonder whether node-red overrides env.get() in subflows in order to support json and other types. In that case presumably the editor has no way of knowing that node-red is overriding it.

@Putzeimer if you try the code in a function outside of a subflow does the editor still complain. It doesn't matter that the code will not run, it is just the syntax checker that we are testing.

[Edit] Ignore the last paragraph, of course it will complain, and in that case is right to do so.

Sorry, saw the example flow and thought it was a download link.

Have you tried declaring List as an array first?
e.g.

let List = [];
List = env.get('List');
node.warn(typeof List);
node.warn(Array.isArray(List));
node.warn(List);
for (let i in List)
    node.warn(List[i]);
return msg;

Yes, I guess that is exactly the point. I updated the topic to indicate that it is only subflow related.

Maybe it would be cleaner to use two seperate functions. Something like this:
env.get() to get a regular environment variable of type string but not subflow environment variables.
subflow.get() to get subflow environment variables of type any but not regular environment variables.

So in function nodes (no matter if in subflow ot not) you could use:
env.get('NR_NODE_NAME');
And in function nodes only inside a subflow:
subflow.get('List');
The downside of this change would be the lack of downward compatibility. So perhaps one of the developers comes up with a better idea?

Indeed, the editor then interpretes List as a string even though it's not. I don't get why that's working. However, this is just another workaround and doesn't address the problem at it's core.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.