Closure compiler typing: Refer to function (factory method) - javascript

Suppose I have a class with a factory method:
export class Foo {
constructor(options) {
this.a = options.a;
this.b = options.b;
}
/**
* #param {{
* a: number,
* b: number
* }} options
* #return {!Foo}
*/
static create(options) {
return new Foo(options);
}
}
I want to dependency inject Foo's factory method in another class, like:
/**
* #param {{
* createFoo: !function(!Object): !Foo
* }} options
*/
The problem: Closure Compiler says this does not match the formal parameter.
found : {
createFoo: function ({a: number, b: number): Foo,
}
required: {
createFoo: function (Object): Foo,
}
Obviously I can rewrite the type signature and hard code in the record, but I really want to refer to Foo.create so there's no need to update the entire codebase every time I add a new param to the options object.
How can I do this for CC?

One way is to write the type once in a typedef, and then refer to it in both places. (I haven't tried this but I think it should work)
/** #typedef {function({a: number, b:number}): Foo} */
var FooCreator;
export class Foo {
...
/**
* #type {FooCreator}
*/
static create(options) {
return new Foo(options);
}
}
/**
* #param {{
* createFoo: FooCreator
* }} options
*/

The general problem is that you are requiring a function that is more accepting than the Foo.create method. Nick Santos wrote a good explanation of this:
http://closuretools.blogspot.com/2012/06/subtyping-functions-without-poking-your.html
Using a loose function type "Function" or "!Function" will work as you would hope.
I used this definition to verify:
/** #param {{createFoo: !Function}} options */
function f(options) {}
f({createFoo: Foo.create});
CC debugger sample

Related

Overwrite JSDoc in extending module class without redeclaration

Assumed this ...
`use strict;`
class Foo
{
/**
* Description
* #param {Type} arg blabla
*/
doSomething( arg )
{
}
}
class Bar extends Foo
{
/**
* Overwrite the description for `doSomething()` without redeclaring the method itself.
* #param {Type} arg blabla
*/
// dosomething( arg )
// {
// }
}
This is a very simplified nonsense example. The reason in reallife is ...
I have a common collection which accepts mixed elements for some methods like add, replace, remove, etc.. I have a second collection specialized for managing HTTP headers. E. g. I like to change the doc of the add() from "Adds an element to the collection" to "Adds an HTTP header to the collection". The method itself stays as is.
I tried
class Bar extends Foo
{
/**
* Overwrite the description for `doSomething()` without redeclaring the method itself.
* #param {Type} arg blabla
* #method dosomething
* #memberOf {Foo}
*/
}
in various ways ... with #override, #inheritdoc, etc.
How to achieve this?

How do you extend an abstract class in javascript and annotate for closure compiler but without closure library?

Say I have an abstract class
/**#constructor
* #abstract*/
function AbsFoo(){}
/**#return {number}
* #param {number} a
* #param {number} b */
AbsFoo.prototype.aPlusB = function(a,b){
return a + b
};
/**#abstract
* #return {number}
* #param {number} c
* #param {number} d */
AbsFoo.prototype.cMinusD = function(c,d){}; //extending class need to implement.
and i want to extend this class, normally, I would do something like
/**#constructor
* #extends {AbsFoo} */
function Foo(){
AbsFoo.apply(this);
}
Foo.prototype = new AbsFoo();
Foo.prototype.constructor = Foo;
Foo.prototype.doSomething = function(c,d){
return c - d;
};
But closure compiler says
JSC_INSTANTIATE_ABSTRACT_CLASS: cannot instantiate abstract class
refering to the line Foo.prototype = new AbsFoo();
So how would I do this in a way that would keep the prototype inheritance, and the ability to use instanceof all the way up the class chain, but also make the compiler happy?
I use goog.inherits in this situation. Since you don't want to use closure library you could copy just goog.inherits from
closure-library/closure/goog/base.js. Maybe give it the name googInherits. The code is then something like this:
/**#constructor
* #extends {AbsFoo}
*/
Foo = function(){
AbsFoo.apply(this);
}
googInherits(Foo, AbsFoo);
/** #inheritDoc */
Foo.prototype.cMinusD = function(c,d){
return c - d;
};

How can I correctly document instance members added via Object.defineProperties?

I have a class which defines a few instance properties via Object.defineProperties and I'm having great difficulty getting JSDoc 3 to recognize that they belong to their class.
Here's a simplified version of what I'm working with:
/** #exports mymodule */
function mymodule(exports) {
/** #constructor
* #param {String} foo A foo.
* #param {String} bar A bar.
* #classdesc Has a foo and a bar.
*/
function Example(foo, bar) {
Object.defineProperties(this, {
/** A foo and a bar
* #memberof Example
*/
foobar: { enumerable: false, value: foo + bar, writable: false }
});
}
exports.Example = Example;
}
When I run JSDoc, I get output for mymodule, Example, foo, and bar, but not foobar. If I remove the #memberof tag for foobar, it get registered as a global. I've tried #memberof mymmodule~Example, adding #lends to both the Object.defineProperties call and the object passed to it, and converting it to Object.defineProperty, but the results don't change.
How can I document foobar as belonging to Example?
After digging through every example I could find, I finally assembled the necessary incantation — #memberof is indeed the trick, but JSDoc seems to require that modules being used in namepaths be explicitly marked as such. The following worked perfectly:
/** A foo and a bar
*
* #type String
* #instance
* #memberof module:mymodule~Example
*/
You could also try the #lends annotation in place of #memberOf like this :
Object.defineProperties(this, /** #lends Example# */{
/** A foo and a bar */
foobar: { enumerable: false, value: foo + bar, writable: false }
});
Don't forget the sharp symbol after the class name to tell jsdoc members are instance members and not static members.
After fiddling around for many hours, I finally got it to work with #memberOf!. Note the uppercase "O" and the bang "!"
/**
* Description
* #memberOf! MyClass
* #type {String}
*/
var myString;
In case you want to use defineProperty instead, a #type annotation is sufficient (at least for IntelliJ to infer the proper type for the property):
/** #type SomeType */
Object.defineProperty(this, "someProperty", {
get: () => new SomeType()
});

How to document return in JavaScript

I'm writing my own library for the project at work for a browser application and I am having the same old problem deciding how to comment the code.
I'm trying to follow the JsDoc syntax, but will probably continue the Google Closure Compiler way. I may end up using two #return and #returns tags in the documentation, just for portability sake (when I setup the auto-generation of the documentation).
Now, the question, how do you document the return of a custom anonymous object from a function? For example:
return {
username: 'username',
password: 'password',
enabled: true
};
JsDoc has an example of how a #param can be documented to expect object with certain fields, but not the #returns tag. Similarly, the Google Closure Compiler documentation of a Record Type is vague and has no example to work it out.
The Closure-compiler uses a subset of the JSDoc annotations (and adds a few of its own). See the annotation reference for the compiler for the complete set. A JSDoc annotation is similar to a JavaDoc annotation and is a comment block that begins with /** (two stars). While each line of the comment often begins with it's own *, that is a convention that is not required. Only one JSDoc tag is allowed per line, but the arguments for a tag can span multiple lines.
The annotation typically applies to the following statement. Here are some examples:
Variable
/** #type {string} */ var a;
Type Cast
var b = /** #type {string} */ (window['foo']);
note the extra parenthesis
Named Function
/**
* #param {string} bar
* #return {boolean}
*/
function foo(bar) { return true; }
Function Expressions
/** #type {function(string):boolean} */
var foo = function(bar) { return true; }
var foo2 =
/**
* #param {string} bar
* #return {boolean}
*/
function(bar) { return true; }
Typedef
Complex types (including unions, and record types) can be aliased for convenience and maintainability using a typedef. These annotations can be long, but can be split over multiple lines for readability.
/** #typedef {{
* foo:string,
* bar:number,
* foobar:number|string
* }}
*/
var mytype;
For your original example, there are several possible ways to annotate such a function return value. One of the most specific and still convenient is the record type:
/** #return {{username:string, password:string, enabled:boolean}} */
function() {
return {
username: 'username',
password: 'password',
enabled: true
}
}
Note the extra {}. Also keep in mind that record types will not prevent property renaming.
This annotation tells the compiler that the function returns an anonymous type with username, password and enabled properties. Other valid options would be to define an interface elsewhere and typecast the return value to be that interface. The least specific annotation would be Object or *.
To see a wide range of possible annotations, take a look at the extern files in the Compiler project.
One of the nice and portable ways to do it is like the following, using return as a keyword. https://github.com/senchalabs/jsduck/wiki/%40return
/**
* #return {object} return An object with the following properties
* #return {string} return.username Some username
* #return {string} return.password Some password
* #return {boolean} return.enabled Is the user enabled?
*/
function getObj () {
return {
username: 'username',
password: 'password',
enabled: true
};
}
If you have to use it in multiple places, you would have to duplicate it, or use #typedef, but I haven't figured out how to add comments to #typedef so I use something like the following
/**
* #typedef {username:string, password:string, enabled:boolean} MyType
*
* username: The name of the user
* password: Some password
* enabled: Is the user enabled?
*/
/**
* #return {MyType}
*/
function getObj () {
return {
username: 'username',
password: 'password',
enabled: true
};
}
You can also try the suggestion here How can I document a type in webstorm using just jsdoc?
If put this in top of the function
function myFunction() {
/**
* Description of my function
* #return {!Object.<string, string|boolean>} Returns an object containing username, password and enabled information
*/
// Do stuff
return {
username: 'username',
password: 'password',
enabled: true
}
}

Commenting JavaScript for Google Closure Compiler for Singleton

I am trying to understand the JSDoc style for documenting JavaScript that is used with the JavaScript Closure Compiler. I have the JavaScript code below
// ==ClosureCompiler==
// #compilation_level ADVANCED_OPTIMIZATIONS
// ==/ClosureCompiler==
(function(){
/**
* #type Array.<string>
* #private
*/
var sb = [];
/**
* #const
* #type{{append: function(string): SingletonStringBuffer, toString: function(): string}}
*/
window['SingletonStringBuffer'] = {
/**
* #param {string} text
* #return {SingletonStringBuffer}
*/
append: function(text){
sb.push(text);
return SingletonStringBuffer;
},
/**
* #return {string}
*/
toString: function(){
return sb.join("");
}
};
}());
When I do an advanced compile on this code I am receiving 2 warnings.
JSC_TYPE_PARSE_ERROR: Bad type annotation. Unknown type SingletonStringBuffer at line 10 character 35
* #type{{append: function(string): SingletonStringBuffer, toString: function()...
^ JSC_TYPE_PARSE_ERROR: Bad type annotation. Unknown type SingletonStringBuffer at line 15 character 11
* #return {SingletonStringBuffer}
^
The function append returns a deference to the encapsulating object. The variable that it is returning ,SingletonStringBuffer, is declared... so I am not sure what is wrong or how to correct it.
You haven't created a named type as far as the compiler is concerned. For this case, I would expect you to create an interface:
/** #interface */
function StringBuffer() {
}
/**
* #param {string} text
* #return {StringBuffer}
*/
StringBuffer.prototype.append;
etc
This can be declared either in the code (if you are using advanced mode it will be stripped) or in your extern files (if you want the type without the code in simple mode).
You can then use it like so (in your case):
(function(){
/**
* #type Array.<string>
* #private
*/
var sb = [];
/**
* #const
* #type {StringBuffer}
*/
window['SingltonStringBuffer'] = {
/**
* #param {string} text
* #return {StringBuffer}
*/
append: function(text){
sb.push(text);
return SingltonStringBuffer;
},
/**
* #return {string}
*/
toString: function(){
return sb.join("");
}
};
}());
singletons work differently in closure. I have not seen an explicit annotation for it, but the compiler (in advanced mode) has some understanding of certain built-in functions
Singletons would be declared via the goog.addSingletonGetter function, here is a code sample
/**
* #constructor
* #extends {path.to.BaseClass}
*/
path.to.MyClass = function() {
goog.base(this);
};
goog.inherits(path.to.MyClass, path.to.BaseClass);
goog.addSingletonGetter(path.to.MyClass);
and that be it.
PS
you are getting the bad annotation because {SingltonStringBuffer} is never declared as a class.
PPS.
Some rambling on post the fact.
I suspect (but this is untested) that making the constructer private might work. Notice the trailing underscore in the example
/**
* #private -> NOTE THIS IS IN NO WAY VERIFIED
* #constructor
* #extends {path.to.BaseClass}
*/
path.to.MyClass_ = function() {
goog.base(this);
};
goog.inherits(path.to.MyClass, path.to.BaseClass);
goog.addSingletonGetter(path.to.MyClass);

Categories