Google Closure Compiler, how to handle JSC_INEXISTENT_PROPERTY gracefully? - javascript

I'm using a design pattern that uses the return statement to expose public class methods.
Problem is: I'm getting a lot of JSC_INEXISTENT_PROPERTY warnings in Closure Compiler's Advanced mode, which makes it difficult to check the warnings that actually matter.
Example of the pattern I use:
// ==ClosureCompiler==
// #compilation_level ADVANCED_OPTIMIZATIONS
// ==/ClosureCompiler==
/**
* #constructor
*/
var MyClass = function() {
var someFunc = function(myString) {
console.log(myString);
}
return {
myPublicFunc: someFunc
};
}
var myClassInstance = new MyClass();
myClassInstance.myPublicFunc('Hello World');
Warnings:
JSC_INEXISTENT_PROPERTY: Property myPublicFunc never defined on MyClass \
at line 16 character 0
myClassInstance.myPublicFunc('Hello World');
Output (formatted):
(new function() {
return {
a: function(a) {
console.log(a)
}
}
}).a("Hello World");
Which is weird, because Closure understood what the code was doing and compiled the code correctly, renaming myPublicFunc consistently to a. So why did I get this warning? Am I doing something wrong?
Note: I do not want to turn off these warnings because it would also hide warnings I actually care about. I also do not want to use quoted strings or exports because I do want Closure to compress these.

Your function is annotated incorrectly. It's actually not a constructor and in this case the new keyword is unnecessary. Your function simply returns an anonymous type with a myPublicFunc property.
To annotate such a pattern, you would use the record type:
/** #return {{myPublicFunc: function(string) }} */
var MyClass = function() {
var someFunc = function(myString) {
console.log(myString);
}
return {
myPublicFunc: someFunc
};
};
var myClassInstance = MyClass(); // new keyword not needed
myClassInstance.myPublicFunc('Hello World');
Another annotation option is to create an interface and type-cast the returned object to be that interface. This option would be useful when multiple functions return an object that conforms to the same interface.

You can also use:
/** #type {function(new:{myPublicFunc: function(string)} )} */
var MyClass = function() {...
The function can be called with "new" but doesn't return an instance of "MyClass".

Adding
MyClass.prototype.myPublicFunc = null;
would solve the problem though I don't know whether this is the best solution.
I don't really know how the compiler works, but I could imagine that if you have a constructor function, it expects instance properties to be assigned to this inside the constructor or to MyClass.prototype.
If you remove the #constructor annotation and omit new, then there is not warning (but the compiled code is only console.log("Hello World");.

Related

Angular 2+ variable hoisting?

How does Angular resolve all it's variables regardless of where there placed in a component?
For example in Vanilla JS
console.log(a) // undefined
let a = 'Hello;
Angular component
ngOnInit(){
this.example()
}
example(){
console.log(this.a) // Hello
}
a = 'Hello'
I'm aware that this is bad practice and the compiler will complain about that placement of the variable but none the less I am curious how Angular achieves this, or whether it's not an Angular specific behaviour?
This is not an Angular behavior. Actually the piece of code that you provided is inside a class, and the a is not a variable, actually it's a property.
JavaScript (and Typescript) doesn't requires properties to be declared before methods (neither constructor), since it's just a declaration that will be used futurely when this class will be instantiated.
Although tslint may warn you about the placement of it after methods, it's just a coding style concern.
You may translate a class to a traditional function constructor:
class Car {
make = 'default';
drive() {
/* ... */
}
model = 'foo'
}
can be wrote as (and is converted to when using some polyfill on browsers that doesn't support ES6 Class):
var Car = function() {
this.make = 'default';
this.model = 'foo';
}
Car.prototype.drive = function() {
/* ... */
}
Note that in the second case, the properties are defined inside the constructor, so it will always run before the method be called.

"this" is not returning module.exports

With the code below using Node.js v11.13.0, I receive the error that this.say is not a function.
/* utils.js */
module.exports = {
test: function() {
this.say('Hello.');
},
say: function(text) {
console.log(text);
}
};
/* index.js */
const {test} = require('./utils.js');
test();
When I log this with the function declared using function() {, it seems to return the global environmental object created by Node.js. If I use an arrow function instead, it returns an empty object.
Why isn't this returning module.exports?
The code is only an example, I'm not actually using it.
This has since been resolved by properly using module.exports instead. Part of the confusion behind this question was the fact that Visual Studio Code says this in my function should be module.exports. If I needed to use it multiple times I'd create a class, but I see no use now.
Thank you for the clarification and suggestions.
First off: why are you trying to do this? You're in Node, so there is literally no reason for any of your code to look up what the node construct is. It should only care about being able to find the things you wrote.
Secondly, you're declaring a plain object, not a class instance, so the this keyword will never point to that variable. It can only ever point to the owner of whatever the current execution scope is.
If you want to return module.exports, then just... do. Either directly, which would be weird, or by at least capturing your export as a real thing first:
const mything = {
test: function() {
mything.say("hello");
},
say(text) {
console.log(text);
}
};
module.exports = mything;
But from your code, that's not what you want. What you want is a real class:
class MyThing {
test() {
this.say('Hello.');
}
say(text) {
console.log(text);
}
};
module.exports = MyThing;
Which you should then call as:
const MyThing = require("./my-thing.js");
let mything = new MyThing();
mything.test();

Mixing object construction with application logic

I've started refactoring my code to make it testable. One area I've found a problem is where I've mixed object construction with application logic. If I have a constructor called SomeClass which performs application logic but also instantiates another class I run in to problems when I try to test:
function SomeClass() {
//does lots of application type work
//but then also instantiates a different object
new AnotherClass();
}
Testing becomes difficult because now I need to find a way to create AnotherClass in the test environment.
I've dealt with this problem using dependency injection. So SomeClass takes an instance of AnotherClass as a parameter:
function SomeClass(anotherObj) {
}
Problem with this is as far as I can see is all this does is defer the problem to somewhere else in my application. I still have to create the anotherObj from AnotherClass somewhere else in my code.
This google testing article http://googletesting.blogspot.co.uk/2008/08/by-miko-hevery-so-you-decided-to.html suggests:
In order to have a testable code-base your application should have two
kinds of classes. The factories, these are full of the "new" operators
and are responsible for building the object graph of your application,
but don't do anything.And the application logic classes which are devoid of the "new" operator and are responsible for doing work.
This sounds exactly like my problem and the factory type pattern is what I need. So I tried something like:
function anotherClassFactory() {
return new AnotherClass();
}
function SomeClass() {
anotherClassFactory();
}
But then SomeClass still has a dependency on the factory. How do I get around this correctly?
(I'm making this a community wiki answer because I frankly think it only answers part of the question, while leaving too much unsaid. Hopefully others with more knowledge can improve it.)
But then SomeClass still has a dependency on the factory. How do I get around this correctly?
According to this article linked by the one you linked, you do it like this:
anotherclass.js:
function AnotherClass() {
}
AnotherClass.prototype.foo = function() { /* ... */ };
AnotherClass.prototype.bar = function() { /* ... */ };
AnotherClass.prototype.baz = function() { /* ... */ };
someclass.js:
function SomeClass(a) {
// ...app logic...
// Use AnotherClass instance `a`; let's say you're going to call `foo`,
// but have no use for `bar` or `baz`
a.foo();
// ...app logic...
}
someclass-test.js:
function someClass_testSomething() {
var sc = new SomeClass({
foo: function() { /* ...appropriate `foo` code for this test... */}
});
// ...test `sc`...
}
function someClass_testSomethingElse() {
// ...
}
app.js:
function buildApp() {
return {
// ...lots of various things, including:
sc: new SomeClass(new AnotherClass())
};
}
So the real app is built using buildApp, which gives SomeClass its AnotherClass instance. Your tests for SomeClass would use someClass_testSomething and such, which uses the real SomeClass but a mocked-up instance rather than a real AnotherClass containing just enough of it for the purposes of the test.
My dependency-injection-fu is weak, though, and I frankly don't see how buildApp scales to the real world, nor do I see what you're supposed to do if a method has to create an object to do its job, e.g.:
SomeClass.prototype.doSomething = function() {
// Here, I need an instance of AnotherClass; maybe I even need to
// create a variable number of them, depending on logic internal
// to the method.
};
You're not going to pass everything the method needs as arguments, it would be a spaghetti nightmare. This is probably why for more static languages, there are usually tools rather than just coding patterns involved.
In JavaScript, of course, we have another option: Just go ahead and use new AnotherClass in the code:
anotherclass.js:
function AnotherClass() {
}
AnotherClass.prototype.foo = function() { /* ... */ };
AnotherClass.prototype.bar = function() { /* ... */ };
AnotherClass.prototype.baz = function() { /* ... */ };
someclass.js:
function SomeClass() {
// ...app logic...
// Use AnotherClass instance `a`; let's say you're going to call `foo`,
// but have no use for `bar` or `baz`
(new AnotherClass()).foo();
// ...app logic...
}
someclass-test.js:
var AnotherClass;
function someClass_testSomething() {
// Just enough AnotherClass for this specific test; there might be others
// for other tests
AnotherClass = function() {
};
AnotherClass.prototype.foo = function() { /* ...appropriate `foo` code for this test... */};
var sc = new SomeClass();
// ...test `sc`...
}
function someClass_testSomethingElse() {
// ...
}
You use anotherclass.js and someclass.js in your real app, and you use someclass.js and someclass-test.js when testing SomeClass.
That's a rough sketch, of course; I'm assuming your real-world app probably doesn't have globals (SomeClass, AnotherClass) all over the place, but however it is you're containing SomeClass and AnotherClass can presumably also be used to contain SomeClass, and to contain the tests for it and their various fake AnotherClasss.

How do I tell the closure compiler to ignore code?

I decided I needed something to help me a bit while implementing interfaces. So I added this function to the base.js file in the closure library.
/**
* Throws an error if the contructor does not implement all the methods from
* the interface constructor.
*
* #param {Function} ctor Child class.
* #param {Function} interfaceCtor class.
*/
goog.implements = function (ctor, interfaceCtor) {
if (! (ctor && interfaceCtor))
throw "Constructor not supplied, are you missing a require?";
window.setTimeout(function(){
// Wait until current code block has executed, this is so
// we can declare implements under the constructor, for readability,
// before the methods have been declared.
for (var method in interfaceCtor.prototype) {
if (interfaceCtor.prototype.hasOwnProperty(method)
&& ctor.prototype[method] == undefined) {
throw "Constructor does not implement interface";
}
}
}, 4);
};
Now this function will throw an error if I declare that my class implements a interface but doesn't implement all the interface's methods. This has absolutely no gain from the end user's perspective, it is simply a nice addition to help the developer. Consequently how do I tell the closure compiler to ignore the below line when it sees it?
goog.implements(myClass, fooInterface);
Is is possible?
It depends on what you mean by ignore. Do you want it to compile down to nothing so that it only works in uncompiled code? If so, you can use one of the standard #define values:
goog.implements = function (ctor, interfaceCtor) {
if (!COMPILED) {
...
}
};
or alternately, only when goog.DEBUG is enabled:
goog.implements = function (ctor, interfaceCtor) {
if (goog.DEBUG) {
...
}
};
if these don't fit you can define your own.
Or do you mean something else entirely?

Annotate Singleton objects in JavaScript for the Google Closure Compiler, or "dangerous use of the global this object" warning

I'm working with the Google Closure Compiler in ADVANCED_OPTIMIZATIONS compilation level and have started to annotate my constructors because I get all kinds of warnings:
WARNING - dangerous use of the global this object
For my 'constructor' type functions I'll annotate them like this:
/**
* Foo is my constructor
* #constructor
*/
Foo = function() {
this.member = {};
}
/**
* does something
* #this {Foo}
*/
Foo.prototype.doSomething = function() {
...
}
That seems to work fine, however what if I have a 'singleton' object that isn't constructed with var myFoo = new Foo();
I couldn't find in the documentation how to annotate this type of object because its type is just object right?
Bar = {
member: null,
init: function() {
this.member = {};
}
};
The preferred way of creating singletons in Closure is like this:
/** #constructor */
var Bar = function() { };
goog.addSingletonGetter(Bar);
Bar.prototype.member = null;
Bar.prototype.init = function() {
this.member = {};
};
This allows for lazy instantiation of the singleton. Use it like this:
var bar1 = Bar.getInstance();
var bar2 = Bar.getInstance();
bar1.init();
console.log(bar2.member);
Keep in mind that this doesn't prevent people from using the constructor to create instances of Bar.
This is exactly the type of potential bug that "dangerous use of this" warns you against. In your example, the Closure Compiler may try to "flatten" your code to:
Bar$member = null;
Bar$init = function() { this.member = {}; };
NOTE: The Closure Compiler currently will not flatten a namespace that is declared as a global object (i.e. without the "var" keyword in front), so your code may still work now. However, there is no telling that it won't do that in a future version and your code will suddenly break without warning.
Of course, then "Bar$member" and "Bar$init" will be renamed to "a" and "b" respectively. This is called "namespace flattening" or "collapsing of properties".
You can immediately see that your code no longer works correctly. Before compilation, if you write:
Bar.init();
this will refer to Bar. However, after compilation it becomes:
Bar$init();
this will no longer refer to Bar. Instead it refers to the global object.
This is way the compiler is trying to warn you that using "this" in such a way is "dangerous", because "this" may be changed to refer to the "global" object. That's the true meaning of the warning.
In short, DO NOT DO THIS. This type of coding style creates bugs that are very difficult to track down.
Modify your code this way:
var Bar = { // Closure Compiler treats globals and properties on global differently
member: null,
init: function() { Bar.member = {}; }
};
or use a closure:
var Bar = (function() {
var member = null;
return {
init: function() { member = {}; }
};
})();
When using the Closure Compiler in Advanced Mode, do not try to get rid of warnings by annotating them away. Warnings are there for a reason -- they try to warn you about something.

Categories

Resources