0

I keep getting the warning mentioned above but can't figure out why. I've added deps.js and deps.js contains a reference to the type.

Here is the offending code:

goog.provide("MyCompany.MyApp.dom");


MyCompany.MyApp.dom.getArticleAmountInput_=function(){
    return document.getElementById("articleAmount");
};
/**
 * Gets the article amount input value
 * @type {function(this:MyCompany.MyApp.dom):number} */
MyCompany.MyApp.dom.getArticleAmount=function(){
    var tmp=this.getArticleAmountInput_();
    return (tmp)?tmp.value():undefined;
};

In deps.js:

goog.addDependency('../../MyCompany/MyApp/dom.js', [ 'MyCompany.MyApp.dom'], []);

Code loads in html so it does find the class at runtime. Here is how I compile the code:

java -jar ../../compiler.jar \
--compilation_level=ADVANCED_OPTIMIZATIONS \
--formatting=PRETTY_PRINT \
--warning_level=VERBOSE \
--summary_detail_level=3 \
--js=MyCompany/MyApp/dom.js \
--js=closure/goog/deps.js \
--js_output_file=out.js

And it keeps giving me the warning:

WARNING - Bad type annotation. Unknown type MyCompany.MyApp.dom

[update]

Tried to leave out the goog.provide completely and leave out the js=closure/goog/deps.js when compiling and changed everything to lower case but keep getting the same warning on this code:

//goog.provide("mycompany.myapp.dom");
var mycompany={};
mycompany.myapp={};
mycompany.myapp.dom={};

mycompany.myapp.dom.getArticleAmountInput_=function(){
    return document.getElementById("articleAmount");
};
/**
 * Gets the article amount input value
 * @type {function(this:mycompany.myapp.dom):number} */
mycompany.myapp.dom.getArticleAmount=function(){
    var tmp=this.getArticleAmountInput_();
    return (tmp)?tmp.value():undefined;
};

[update]

The thing is that when I add a typedef I get warnings that myapp.dom is never defined but when I let the code determine what the type is I get Bad type annotation.

Tried to add a typedef like so:

/**
 * @typedef {{getArticleAmount:function(this:myapp.dom):?number}}
 */
myapp.dom;

But don't realy see why as goog.provide should create the myapp.dom and the compiler should know that.

[update]

I have created the following myapp.workflow from constructors. Now the compiler should recognize the type and Eclipse can code assist. Not sure if this is the way to do it but I'll try to re factor a small project.

Setting up the main types in types.js

// source: js/mmyapp/types.js
goog.provide("myapp.types");
/** @constructor */
var gooblediegoog=function(){};
/** @constructor */
gooblediegoog.prototype.WorkFlow=function(){};
/** @constructor */
gooblediegoog.prototype.Dom=function(){};
myapp.types=new gooblediegoog();

A file that isn't used at all in my code but tells Eclipse how to auto complete:

// source: js/myapp/forCodeAssist.js
/** @const {boolean} */
var ALLWAYSFALSE=false;

if(ALLWAYSFALSE){
    /**needed for Eclipse autocomplete
     * @constructor
     */
    var MyApp=function(){};
    MyApp.prototype.types=new gooblediegoog();
    Window.prototype.myapp=new MyApp();
    MyApp.prototype.workflow=new myapp.types.WorkFlow();
    MyApp.prototype.dom=new myapp.types.Dom();
}

An implementation of workflow:

// source: js/myapp/workflow.js
goog.provide("myapp.workflow");
goog.require("myapp.types");
goog.require("myapp.dom");

/** @returns number|undefined */
myapp.types.WorkFlow.prototype.createOrder=function(){
    return myapp.dom.getArticleAmout();
};
myapp.workflow=new myapp.types.WorkFlow();
window['console'].log(myapp.workflow.createOrder());

This can be converted to a myapp.workflow.createOrder=... syntax by replacing myapp.types.WorkFlow.prototype with myapp.workflow, removing myapp.workflow=new myapp.types.WorkFlow() and removing the goog.require("myapp.types"). Maybe this can be automated in the build/compile process if needed.

I am not sure if creating a single object with the help from a constructor function is much more expensive than just having goog.require create myapp.workflow and adding properties to it as I used to do (and as it's done in closure library).

HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    why are none of these functions on the prototype? are you using them as mixins? – lennel Jun 14 '13 at 11:35
  • There is only one mycompany.myapp.dom. Wans't aware that properties of a single instance have to declared on the prototype. Took the example from array.js where they use the `goog.array.something=something` syntax. None of the properties of goog.array are added with goog.array.prototype. I thought you use prototype to "subclass" or when you expect there to be more instances (like constructor function). – HMR Jun 14 '13 at 12:38
  • @lennel I think I see the cause of the problem. Compiler doesn't like it when the type definition and the implementation are the same thing. I tried passing `goog.array` as a variable to a function that had `@param {goog.array} ga goog.array` and get the same warning `bad type annotation`. Tried to let the compiler know what `this` was because it keeps on nagging about `dangerous use of this in static method` In the book I was reading the solutin was to set a @this annotation. I think I'll just give up on it for now as it seems too much trouble. – HMR Jun 14 '13 at 13:21
  • about array, you would do this {Array.} where type and anothertype are whatever you are planning on inserting into the array. – lennel Jun 14 '13 at 14:37
  • I mean the syntax of `goog.array` is an object literal. It has properties like `indexof` and `lastIndexOf` Object literals don't have a type so you can't say the `this` context of it's functions is `goog.array` so when I would implement indexof I have to write out `goog.array.someProp` every time instead of `this.someProp` or the compiler will complain about the `this` context. I typed out the full namespace before but was just wondering why I could not use `this` in my functions but think I understand now. – HMR Jun 14 '13 at 23:51

1 Answers1

1

Anonymous types (namespaces or singletons) don't have specific types in Closure-compiler and cannot be used as a type name in an annotation. Only constructors and interfaces can be used as new type names. (typedefs can be used but are really just shorthand annotations)

In many cases the compiler will correctly infer the types and you will not need to annotate it. In your specific case, it looks like the problem is your use of the this keyword. Using the this keyword in a static object / namespace is dangerous and not supported. The compiler will raise a warning. While you can suppress the warning with a @this annotation, that is not the correct action here as you are not specifically specifying the this reference by using .call or .apply.

The solution is to use the full object name:

/**
 * Gets the article amount input value
 */
mycompany.myapp.dom.getArticleAmount=function(){
  var tmp=mycompany.myapp.dom.getArticleAmountInput_();
  return (tmp)?tmp.value():undefined;
};

For the full technical details, reference the This this is your this post.

Chad Killingsworth
  • 14,360
  • 2
  • 34
  • 57
  • Is there a way I can use a constructor function and still use `goog.provide`? That way I can define the type of the object and still let the compiler know what object the file provides (for calcldeps.py and base.js in uncompiled code). This would be nice because Eclipse seems to like constructor functions for it's code assist. – HMR Jun 14 '13 at 13:26
  • Sure. Best example is closure library code such as http://docs.closure-library.googlecode.com/git/closure_goog_fx_dom.js.source.html - See something like `goog.fx.dom.PredefinedEffect`. – Chad Killingsworth Jun 14 '13 at 13:32
  • I am struggling a bit, my dom object is more like goog.array and there should be only one of them as `myapp.dom` is more a namespace than a constructor. Since I need the constructor function syntax for both the compiler to figure out what type it is and for Eclipse to handle code assist I can't use the syntax used in `goog.array`. So I need to write it as a constructor but don't need to initiate more than one instance. I'll fiddle with this a bit and see what I can come up with. – HMR Jun 14 '13 at 13:56
  • I've updated my answer with code for `myapp.dom` and `myapp.workflow` will try to refactor one "class" in a small project I made to see if this works. Thank you for your help. – HMR Jun 14 '13 at 14:32