Recursive types in closure compiler - javascript

Is there a way to define a recursive type in closure compiler's typing syntax? In other words, could I define a type that includes itself in its definition?
/**
* A node on the tree.
* #type {{left: (Tree|null)}}
*/
var Tree = {
left: null
};
(side note: yes, this definition of a tree is wrong)
Although the above compiles with simple optimizations, it fails with advanced throwing the following error:
JSC_TYPE_PARSE_ERROR: Bad type annotation. Unknown type Tree at line 3 character 17
* #type {{left: (Tree|null)}}

Just a guess but can you put the type over the left part.
/**
* Not sure what goes here
*/
var Tree = {
/**
* #type {Tree}
*/
left: null
};
Also don't you need a constructor for a type
/**
* #constructor
*/
var Tree = function(){
this.left = new Tree()
};
/**
* #type {Tree}
*/
Tree.prototype.left = null;
Just so you know, you've now got infinite recursion if you instantiate the type with
var test = new Tree();

/**
* #typedef{{left:Tree}}
*/
var Tree;
or
/**
* #interface
*/
var Tree =function(){}
/** #type {Tree} */
Tree.prototype.left;

Related

js-doc / google-closure-compiler how to document passed enum object itself?

e.g.
/**
* Super my enum
* #enum {number}
*/
var MyEnum = {
ONE: 1,
TWO: 2
};
/**
* #param {what type is it?} enumObj
*/
function showEnum(enumObj) {
console.log(enumObj);
}
//show the enum definition object
showEnum(MyEnum);
how to describe parameter type as not the value/instance of MyEnum, but as the MyEnum object itself?
Use !MyEnum where the ! means "non-null".
/**
* #param {!MyEnum} enumObj
*/
function showEnum(enumObj) {
console.log(enumObj);
}
the solution i tested in WebStorm and VSCode for autocompletion is to use typeof MyEnum.
Its still invalid JSDoc, but supported for autocompletion by IDEs.
/**
* #param {typeof MyEnum} enumObj
*/
function showEnum(enumObj) {
console.log(enumObj);
}

Using JSDoc with AMD

I have the following AMD structure for my modules.
/* globals define */
define([""], function() {
'use strict';
var module = {};
function _somePrivateFunc() {}
function somePublicFunc() {}
module.somePublicFunc = somePublicFunc;
return module;
});
I'm struggling with JSDoc to make it work with my modules. Until now, I could make it work like
/* globals define */
/**
* Module description here
* #module some/module/name
*/
define([""], function() {
'use strict';
/**
* #alias module:some/module/name
*/
var module = {};
function _somePrivateFunc() {}
function somePublicFunc() {}
/**
* Some property description here
* #itWorks
*/
module.somePublicFunc = somePublicFunc;
return module;
});
As you can see, it's working. But I want my doc comments to be defined on the function declarations instead where I add them to the module. I want to achieve this:
/* globals define */
/**
* Module description here
* #module some/module/name
*/
define([""], function() {
'use strict';
/**
* #alias module:some/module/name
*/
var module;
/**
* Somehow make this appear in JSDoc
* #extend module:some/module/name
* #private
*/
function _somePrivateFunc() {}
/**
* Make this appear in JSDoc for sure.
*/
function somePublicFunc() {}
// I want this to NOT annotate, and I want the property to gather JSDoc
// from the func. declaration
module = {
somePublicFunc: somePublicFunc
};
return module;
});
The latter one is what I would like to achieve, but sadly it's not working :(
Any ideas?
Cheers
Edit:
I made some progress. Mind, that if I write only this:
define('dep', function(require) {
/**
* #exports dep
*/
var module = {};
/**
* Adds two numbers together.
* #param {Number} a the first number
* #param {Number} b the second number
* #return {Number} the sum of a and b
*/
function sum(a, b) {
return a + b;
}
module.sum = sum;
return module;
});
then the output is empty. JSDoc can't generate the documentation properly. Although if I add a #method <name> annotation to my function, then it automagically works great. Interesting, but adding only a #method annotation without a name is not sufficient enough. My final working solution looks like:
define('dep', function(require) {
/**
* #exports dep
*/
var module = {};
/**
* Adds two numbers together.
* #func sum
* #param {Number} a the first number
* #param {Number} b the second number
* #return {Number} the sum of a and b
*/
function sum(a, b) {
return a + b;
}
/**
* Creates a closure. Adds two numbers together
* #func _add
* #param {Number} a the closure variable to add statically
* #return {Function} the closure
* #private
*/
function _add(a) {
return function(b) {
return a + b;
};
}
module.sum = sum;
return module;
});
The CLI flag -p makes the private members to appear in the documentation.
Additional info
I was able to make inner links only one way through my sample code:
/**
* Adds two numbers together.
* #func sum
* #param {Number} a the first number
* #param {Number} b the second number
* #return {Number} the sum of a and b
* #see {#link module:dep~_add|_add}
*/
function sum(a, b) {
return a + b;
}

Google closure compiler, JSC_INEXISTENT_PROPERTY issue with mixin/extend

The code below throws a warning/error when compiled with google closure compiler advanced mode.
JSC_INEXISTENT_PROPERTY: Property getJerseyNumber never defined on player
Any ideas how we can fix this?
var getDetails = {
getJerseyNumber: function() {
return Math.random();
}
};
/**
* #param {Object} source
* #param {Object} delta
*/
function mixIn(source, delta) {
for (var i in delta) {
source[i] = delta[i];
}
}
/**
* #type {{name: string , sport: string}}
*/
var player = {
name: 'Tom Brady',
sport: 'Football'
};
mixIn(player, /** #lends {player} */ getJerseyNumber);
alert(player.getJerseyNumber());
Using #lends work with object literals only. So adding this line will work, but any other ideas?
mixIn(player, /** #lends {player} */ {
getJerseyNumber: function() {
return Math.random();
}
});
According to the Closure documentation,
#lends
Indicates that the keys of an object literal should be treated as properties of some other object. This annotation should only appear on object literals.
This is described in more detail by jsdoc-toolkit.
For your example, I would use classes for common behavior.
/**
* #constructor
* #param {string} name
* #param {string} sport
*/
var Player = function(name, sport) {
this.name = name;
this.sport = sport;
};
Player.prototype.getJerseyNumber = function() {
return Math.random();
};
var player = new Player('Tom Brady', 'Football');
alert(player.getJerseyNumber());
Of course, I imagine your example has some additional complexity, but without code to represent that complexity, I can only guess what that is.
You may be after the proposed (but as yet nonexistant) #mixin annotation.

Properly type instance variables with closure compiler

I cannot get to properly type the following piece of code:
/**
* #constructor
*/
function F() {
this.a = 0;
};
/**
* #type {function(number)}
*/
F.prototype.g = function(b) {
this.a += b;
};
I get the following warning:
test.js:12: WARNING - could not determine the type of this expression
this.a += b;
^
How can I properly type this in this example?
-- EDIT --
If you want to see the warning, you need to set reportUnknownTypes to trueas explained here. I am trying to get to 100% typed code and I figured I could not reach that for that simple a program.
/** #type {function(number)} */
doesn't specify the "this" type so it is unknown. To specify it in that fashion you would want to use:
/** #type {function(this:F, number)}
Using "#param {number}" lets the compiler infer the "this" type from the fact it was declared on F's prototype.
You seem to need to use #param {number} b instead of #type {function(number)}. Typing with #param doesn't throw the warning. Doesn't make a whole lot of sense, but it works:
/**
* #param {number} b
*/
F.prototype.g = function(b) {
this.a += b;
};
--
Original answer:
I think it's because you didn't type a in your constructor. Try this:
/**
* #constructor
*/
function F() {
/**
* #type {number}
* #private
*/
this.a = 0;
};

How to document a Require.js (AMD) Modul with jsdoc 3 or jsdoc?

I have 2 types of Modules:
Require.js Main File:
require.config({
baseUrl: "/another/path",
paths: {
"some": "some/v1.0"
},
waitSeconds: 15,
locale: "fr-fr"
});
require( ["some/module", "my/module", "a.js", "b.js"],
function(someModule, myModule) {
}
);
Mediator Pattern:
define([], function(Mediator){
var channels = {};
if (!Mediator) Mediator = {};
Mediator.subscribe = function (channel, subscription) {
if (!channels[channel]) channels[channel] = [];
channels[channel].push(subscription);
};
Mediator.publish = function (channel) {
if (!channels[channel]) return;
var args = [].slice.call(arguments, 1);
for (var i = 0, l = channels[channel].length; i < l; i++) {
channels[channel][i].apply(this, args);
}
};
return Mediator;
});
How can i document this with jsdoc3 when possible with jsdoc too?
This is my first answer on SO, please let me know how I can improve future answers.
Your specific example
I've been searching for an answer for this for a good two days, and there doesn't seem to be a way to document RequireJS AMD modules automatically without some redundancy (like repeated function names). Karthrik's answer does a good job of generating the documentation, but if something gets renamed in the code the documentation will still be generated from what's in the jsDoc tags.
What I ended up doing is the following, which is adjusted from Karthik's example. Note the #lends tag on line 1, and the removal of the #name tag from the jsDoc comment blocks.
define([], /** #lends Mediator */ function(Mediator){
/**
* Mediator class
* This is the interface class for user related modules
* #class Mediator
*/
var channels = {};
if (!Mediator) Mediator = {};
/**
* .... description goes here ...
* #function
*
* #param {Number} channel .....
* #param {String} subscription ..............
* #example
* add the sample code here if relevent.
*
*/
Mediator.subscribe = function (channel, subscription) {
if (!channels[channel]) channels[channel] = [];
channels[channel].push(subscription);
};
Mediator.publish = function (channel) {
if (!channels[channel]) return;
var args = [].slice.call(arguments, 1);
for (var i = 0, l = channels[channel].length; i < l; i++) {
channels[channel][i].apply(this, args);
}
};
return Mediator;
});
From what I understand, the #lends tag will interpret all jsDoc comments from the next following object literal as part of the class referenced by the #lends tag. In this case the next object literal is the one beginning with function(Mediator) {. The #name tag is removed so that jsDoc looks in the source code for function names, etc.
Note: I've used the #exports tag at the same place as where I put the #lends tag. While that works, it'll create a module in the docs… and I only wanted to generate docs for the class. This way works for me!
General jsDoc references
jsdoc-toolkit Tag Reference - Great reference for the tags in jsdoc-toolkit. Has a bunch of examples, too!
2ality's jsDoc intro - Comprehensive tutorial based on jsDoc-toolkit.
jsDoc3 reference - Fairly incomplete, but has some examples.
jsDoc doesn't seem to like the "define" and "require" calls.
So, we ended up using multiple tags to make the jsDoc tool to pick up the constructor and other specific class methods. Please have a look at the example below:
I have just copy-pasted from my source-code and replaced it with your class name and method names. Hope it works for you.
define([], function(Mediator){
/**
* Mediator class
* This is the interface class for user related modules
* #name Mediator
* #class Mediator
* #constructor
* #return Session Object
*/
var channels = {};
if (!Mediator) Mediator = {};
/**
* .... description goes here ...
* #name Mediator#subscribe
* #function
*
* #param {Number} channel .....
* #param {String} subscription ..............
* #example
* add the sample code here if relevent.
*
*/
Mediator.subscribe = function (channel, subscription) {
if (!channels[channel]) channels[channel] = [];
channels[channel].push(subscription);
};
Mediator.publish = function (channel) {
if (!channels[channel]) return;
var args = [].slice.call(arguments, 1);
for (var i = 0, l = channels[channel].length; i < l; i++) {
channels[channel][i].apply(this, args);
}
};
return Mediator;
});
Note: The above method of documenting JS-code worked out well for us while using jsDoc. Haven't got a chance to try jsDoc3.
Taking the link from Muxa's answer, we see that the documentation does specifically refer to RequireJS:
The RequireJS library provides a define method that allows you to write a function to return a module object. Use the #exports tag to document that all the members of an object literal should be documented as members of a module.
Module Example
define('my/shirt', function () {
/**
* A module representing a shirt.
* #exports my/shirt
* #version 1.0
*/
var shirt = {
/** A property of the module. */
color: "black",
/** #constructor */
Turtleneck: function(size) {
/** A property of the class. */
this.size = size;
}
};
return shirt;
});
So in the above example, we see that jsdoc will parse a my/shirt module and document it as having two members: a property color, and also a class Turtleneck. The Turtleneck class will also be documented as having it's own property size.
Constructor Module Example
Use the #alias tag simplify documenting a constructor-module in RequireJS.
/**
* A module representing a jacket.
* #module jacket
*/
define('jacket', function () {
/**
* #constructor
* #alias module:jacket
*/
var exports = function() {
}
/** Open and close your Jacket. */
exports.prototype.zip = function() {
}
return exports;
});
The above is what you'd want to use if you are exporting a constructor function as the module which will be used as a class to instantiate objects. To sum up, I'm not sure about using the #lends and other tags/techniques that have been recommended. Instead, I would try to stick with the #module, #exports, and #alias tags used in the documentation referencing RequireJS.
I'm not sure how you should document your requirejs 'main' file. If I understand correctly, you are not actually defining any module there, but rather executing a one off function which depends on several modules.
My AMD classes use a slightly different form, but JSDoc wasn't documenting them either so I thought I'd share what worked for me.
Constructors in the global namespace are automatically added:
/**
* #classdesc This class will be documented automatically because it is not in
* another function.
* #constructor
*/
function TestClassGlobal() {
/**
* This is a public method and will be documented automatically.
*/
this.publicMethod = function() {
};
}
If you want this behavior on a constructor inside an AMD module, declare it either as global or a member of a namespace:
define([], function() {
/**
* #classdesc This won't be automatically documented unless you add memberof,
* because it's inside another function.
* #constructor
* #memberof Namespace
*/
function TestClassNamespace() {
}
/**
* #classdesc This won't be automatically documented unless you add global,
* because it's inside another function.
* #constructor
* #global
*/
function TestClassForcedGlobal() {
}
});
Looks like things have gotten a lot simpler in JSDoc3. The following worked for me:
Mediator as a module
/**
* Mediator Module
* #module Package/Mediator
*/
define([], function(Mediator){
var channels = {};
if (!Mediator) Mediator = {};
/**
* Subscribe
* #param {String} channel Channel to listen to
* #param {Function} subscription Callback when channel updates
* #memberOf module:Package/Mediator
*/
Mediator.subscribe = function (channel, subscription) {
if (!channels[channel]) channels[channel] = [];
channels[channel].push(subscription);
};
/**
* Publish
* #param {String} channel Channel that has new content
* #memberOf module:Package/Mediator
*/
Mediator.publish = function (channel) {
if (!channels[channel]) return;
var args = [].slice.call(arguments, 1);
for (var i = 0, l = channels[channel].length; i < l; i++) {
channels[channel][i].apply(this, args);
}
};
return Mediator;
});
However, I would probably make the following change to the code:
/**
* Mediator Module
* #module Package/Mediator
*/
define([], function(){
var channels = {};
var Mediator = {}
...
Reason is, the module says it defines Mediator, but seems to borrow from some other instance of Mediator. I'm not sure I understand that. In this version, it's clear Mediator is defined by this file and exported.

Categories

Resources