-3

If I have this recursive function

function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
}

When I try to make it a module, I get this error

checkNested is not a function

Can anyone see what I am doing wrong?

function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
};

module.exports.checkNested = checkNested;
Sandra Schlichting
  • 25,050
  • 33
  • 110
  • 162

4 Answers4

2

I can reproduce your problem with this two files.

index.js

const checkNested = require('./check-nested');

console.log(checkNested({}, 0));

check-nested.js

function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
}

module.exports.checkNested = checkNested;

The problem here is that check-nested.js exports an object containing a function. You can't call the exported object. You have to call that contained function. One common way is to destructure the object:

index.js

const { checkNested } = require('./check-nested');

console.log(checkNested({}, 0));

check-nested.js

function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
}

module.exports.checkNested = checkNested;

or to use the property of the object:

index.js

const checkNested = require('./check-nested');

console.log(checkNested.checkNested({}, 0));

check-nested.js

function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
}

module.exports.checkNested = checkNested;

or to only export the function without wrapper object

index.js

const checkNested = require('./check-nested');

console.log(checkNested({}, 0));

check-nested.js

function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
}

module.exports = checkNested;

I prefer the first approach and AFAIK it's the most common way.

jabaa
  • 5,844
  • 3
  • 9
  • 30
0

If you export your function while defining it, it's not available to call as a function within the rest of the module.

This will not work:

module.exports.testFunction = function () { 
  console.log('testFunction')
}

module.exports.mainFunction = function () {
  testFunction() // will throw when importer calls main()
}

You must define testFunction separate from exporting it if you want to be able to call it elsewhere within this same module.

Alternatively, you can reference the function through module.exports, but that seems like a bad idea because it ties callers to the fact that this function is currently public.

module.exports.testFunction = function () { 
  console.log('testFunction')
}

module.exports.mainFunction = function () {
  module.exports.testFunction() // works but icky
}

I think the tail recursion is the problem, but I'm honestly not sure why. Here is a simple runnable demo that follows OP's pattern, and which should work, but fails in precisely the same way:

function countLogger( count ) {
    if( count === 0 ) return
    
    console.log(`count ${count}`)
    return countLogger(count - 1)
}

module.exports.countLogger = countLogger

It defines the function separate from export, using the function keyword. (Just like OP, and just like I think is necessary to make this work.)

It exports the function as a named export rather than the default.

The error reads: "Uncaught TypeError: countLogger is not a function"

This is importantly different than the error I get in my first example, which (1) runs once before failing, and (2) says the function isn't defined, (3) gives a line number. By contrast, this version doesn't even run once, and it doesn't say the function is undefined, it says it's not a function.

Very strange.

Tom
  • 8,509
  • 7
  • 49
  • 78
  • Looks like OP isn't exporting the function directly. The problem is with how they are importing the module/function. – Felix Kling Oct 07 '21 at 14:47
  • OP is calling the function recursively. That's the problem, I think. – Tom Oct 07 '21 at 14:49
  • Not if the function is declared as shown in their example (as a function declaration). Of course if they somehow reassign to `checkNested` then that would be a problem but there is no evidence for that. – Felix Kling Oct 07 '21 at 14:50
  • Fails for me when I try it. – Tom Oct 07 '21 at 14:52
  • I put together a runnable demo, and then "fixed" it, and now I'm getting the same problem as OP. This problem is maybe a little more bizarre than appears at first glance... – Tom Oct 07 '21 at 15:00
  • 1
    *"Here is a simple runnable demo that follows OP's pattern, and which should work, but fails in precisely the same way:"* How are you calling the function? – Felix Kling Oct 07 '21 at 15:22
-4

You need to define module.exports before you can assign into it.

So, these work:

module.exports = {}
module.exports.checkNested = checkNested

OR

module.exports = function checkNested(...) {...}
Tom
  • 8,509
  • 7
  • 49
  • 78
  • 3
    No, you don't need to override `module.exports`. It is default-initialised to an object already (and keeping it intact is important/best practice for circular dependencies). – Bergi Oct 07 '21 at 14:34
  • Can you say more about how this interacts with circular dependencies? This is the first I've heard that there's anything valuable in the default value of `module.exports. – Tom Oct 07 '21 at 14:50
  • https://stackoverflow.com/questions/10869276/how-to-deal-with-cyclic-dependencies-in-node-js https://nodejs.org/api/modules.html#modules_cycles – Bergi Oct 07 '21 at 16:36
-4

Just keep it simple and use it like this:

export function checkNested(obj, level, ...rest) {
  if (obj === undefined) return false;
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
  return checkNested(obj[level], ...rest);
}

And import like this:

import {checkNested} from "./CheckNeste.js";
rafaelpadu
  • 1,134
  • 2
  • 6
  • 20
  • The code in the question is Common JS. Your code is ES6. This change probably solves the problem if ES6 is supported but ES6 isn't always supported. – jabaa Oct 07 '21 at 14:35
  • Thats a good point, but the person tagged ECMA-6, so... – rafaelpadu Oct 07 '21 at 14:39