Uncaught TypeError: (intermediate value)(...) is not a function

Everything works fine when I wrote the js logic in a closure as a single js file, as:

(function(win){
   //main logic here
   win.expose1 = ....
   win.expose2 = ....
})(window)

but when I try to insert a logging alternative function before that closure in the same js file,

 window.Glog = function(msg){
     console.log(msg)
 }
 // this was added before the main closure.

 (function(win){
   //the former closure that contains the main javascript logic;
 })(window)

it complains that there is a TypeError:

Uncaught TypeError: (intermediate value)(...) is not a function

What did I do wrong?


The error is a result of the missing semicolon on the third line:

window.Glog = function(msg) {
  console.log(msg);
}; // <--- Add this semicolon

(function(win) {
  // ...
})(window);

The ECMAScript specification has specific rules for automatic semicolon insertion, however in this case a semicolon isn't automatically inserted because the parenthesised expression that begins on the next line can be interpreted as an argument list for a function call.

This means that without that semicolon, the anonymous window.Glog function was being invoked with a function as the msg parameter, followed by (window) which was subsequently attempting to invoke whatever was returned.

This is how the code was being interpreted:

window.Glog = function(msg) {
  console.log(msg);
}(function(win) {
  // ...
})(window);

To make semicolon rules simple

Every line that begins with a (, [, `, or any arithmetic operator, must begin with a semicolon if you want it to be interpreted as its own line ~ Otherwise, it may combine with the previous line by accident. All other line breaks have implicit semicolons.

That's it. Done.

  • Note that /, +, - are the only valid operators you would want to do this for anyway. You would never want a line to begin with '*', since it's a binary operator that could never make sense at the beginning of a line.

Why?

Consider the following:

func()
;[0].concat(myarr).forEach(func)
;(myarr).forEach(func)
;`hello`.forEach(func)
;/hello/.exec(str)
;+0
;-0

Following the above rules prevent the above from being interpreted as

func()[0].concat(myarr).forEach(func)(myarr).forEach(func)`hello`.forEach(func)/hello/.forEach(func)+0-0

Additional Notes

To mention what will happen: brackets will index, parentheses will be treated as function parameters. The backtick would transform into a tagged template, regex will turn into division, and explicitly +/- signed integers will turn into plus/minus operators.

Of course, you can avoid this by just adding a semicolon to the end of every linebreak, but don't believe that doing this can let you code like a C programmer. Since when you don't end a line with a semicolon, Javascript might implicitly add one on your behalf against your desires. So, keep in mind statements like

return       // Implicit semicolon, will return undefined.
    (1+2);

i        // Implicit semicolon on this line
   ++;   // But, if you really intended "i++;"
         // and you wrote it like this,
         // you need help.

The above case will happen to return/continue/break/++/--. Any linter will catch this with dead-code or ++/-- syntax error (++/-- will never realistically happen).

Finally, if you want file concatenation to work, make sure each file ends with a semicolon. If you're using a bundler program (recommended), it should do this automatically.


Error Case:

var userListQuery = {
    userId: {
        $in: result
    },
    "isCameraAdded": true
}

( cameraInfo.findtext != "" ) ? searchQuery : userListQuery;

Output:

TypeError: (intermediate value)(intermediate value) is not a function

Fix: You are missing a semi-colon (;) to separate the expressions

userListQuery = {
    userId: {
        $in: result
    },
    "isCameraAdded": true
}; // Without a semi colon, the error is produced

( cameraInfo.findtext != "" ) ? searchQuery : userListQuery;