Home > Software engineering >  Why "not a function" error when using module.exports?
Why "not a function" error when using module.exports?

Time:10-08

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;

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:

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(...) {...}

CodePudding user response:

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";
  • Related