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
}
}
Related
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
Thanks to the answer found here:
https://stackoverflow.com/a/19336366/592495
My JavaScript documentation is well-organized and well-formatted. Each namespace is a "parent" of methods contained within. However, navigation is not quite as granular as I would like.
After compiling/rendering using the node.js tool via a simple command (jsdoc file1.js file2.js), the docs are generated into a default template. This default template displays my namespaces in the sidebar navigation, but it does NOT show methods that each contains.
You can fake a list of methods by adding the #class directive to each method, but as we know, they are not really classes.
I would love to see a sidebar navigation like this:
My Project
- namespace 1
- method.a
- method.b
- method.c
-namespace 2
- method.d
- method.e
Any direction to documentation I have overlooked would be greatly appreciated.
[edit to add:]
Upon experimentation, #class does almost exactly what I want but with some exceptions:
It lists classes above namespaces. I don't like that since the namespaces are the "parents" as it were.
JavaScript doesn't have classes in that sense. Not ones that are called "classes" with that nomenclature. It creates a weird disconnect when reading the document to see a list of "classes".
It adds the "new" operator automagically. Not all of the methods have constructors... you can see the problem!
[edit: sample code]
So here's the current structure. Before I annotate it with JSDoc comments, here's the basic approach:
var app = app || {};
app.utils = {
whizbang: function() {},
geegolly: function() {}
};
app.render = {
thestuff: function(params) {},
thethings: function(params) {}
}
}
So, using object literal notation, the top level is a "namespace" for the whole application, but within there are sub-namespaces for different purposes. Here, I have a sub-namespace specific to utilities, and another one specific to rendering. Each can have properties, but more importantly they each contain functions. It is these functions which should appear in the sidebar. Now to flesh it out with my current pattern for JSDoc:
/**
* #file MyApp.js This is an awesome description of MyApp.js
*
* #version 0.1
* #author Greg Pettit
* #copyright 2015
*
*/
/**
* Description of my main namespace!
*
* #namespace app
*/
var app = app || {};
/**
* This is a description of my sweet utilities namespace!
*
* #memberof app
* #type {object}
* #namespace app.utils
*/
app.utils = {
/**
* app.utils.whizbang is an awesome function with magical powers. I sure wish
* it would appear in the sidebar as a member of app.utils!
*
* #memberof app.utils
* #method whizbang
*
* #param {method} [successCallback] Method invoked on successful attempt.
* #param {method} [errorCallback] Method invoked on unsuccessful attempt.
*
*/
whizbang: function(successCallback, errorCallback) { // do utility stuff! }
}
/**
* This is a description of the best rendering namespace ever.
*
* #memberof app
* #type {object}
* #namespace app.render
*/
app.render = {
/**
* app.render.thethings renders the things! I wish it would render to the sidebar...
*
* #memberof app.render
* #method thethings
*
* #param {method} node The node to which thethings are rendered
*
*/
thethings: function(node) { // do rendering stuff! }
}
Have you tried using the #lends tag? An example of your code and doc comments would be helpful here.
Since I don't know what your code looks like, I'll just give an example of how I use JSDoc with our in-house framework, which has lots of idiosyncracies (hey, I didn't write it, I just have to use it).
Just to give some context, we have a context object that can create apps and modules (apps are just modules with a start method):
/** #namespace MyApp */
var MyApp = context.app('myApp').use('module1', 'module2', 'underscore');
We have a dependency injection system for backbone that uses an angular-style pattern for expressing dependencies:
/**
* The constructor for MyModel
* #memberof MyApp
* #param {object?} attrs
* #param {object?} options
* #param {AppUtils} appUtils
* #constructor
*/
MyApp.MyModel = function(attrs, options, appUtils) {
this.options = options;
this.utils = appUtils;
}
// This is injected by the dependency resolver at instantiation time
// No additional JSDoc comments are necessary, it actually gets this right
MyApp.MyModel.prototype = {
idAttribute: 'customId',
defaults: {
customId: '',
name: '',
birthday: null
}
};
// Registers MyModel with the IOC container as 'myModelName'
MyApp.model('myModelName', [
'attrs',
'options',
'appUtils'
MyApp.MyModel
]);
And then a different file can have an instance of myModelName injected by adding it to that dependency array at the bottom.
Funny thing is, JSDoc actually does a pretty good job of understanding that particular arrangement, as long as I don't try to get too fancy... but the following pattern is apparently too confusing for it:
/**
* #memberof MyApp
* #param {MyApp.MyModel} myModel
* #param {urlParser} urlParser
* #constructor
*/
MyApp.SomeService = function(myModel, urlParser) {
return {
foo: function() {
//whatever
},
bar: function() {
//whatever
}
};
};
MyApp.service('someModuleName', [
'myModelName',
'urlParser',
MyApp.SomeService
]);
The only thing that I've found that gives me anything close to the desired output is using the #lends tag to tell JSDoc that a particular object/property/method is "lended" as a different property. For example, to document the attributes property of a backbone model (which is ostensibly defined by its defaults property), we do this:
MyApp.MyModel.prototype = {
idAttribute: 'customId',
/** #lends MyApp.MyModel.prototype.attributes */
defaults: {
customId: '',
name: '',
birthday: null
}
};
And for that case where the service is returning an object, the only way we've found to get those object properties documented is like this:
/**
* #memberof MyApp
* #param {MyApp.MyModel} myModel
* #param {urlParser} urlParser
* #constructor
*/
MyApp.SomeService = function(myModel, urlParser) {
/** #lends MyApp.SomeService.prototype */
return {
foo: function() {
//whatever
},
bar: function() {
//whatever
}
};
};
I have no idea if any of that was useful, but maybe it'll give you some ideas for things you could try with #lends. If you can provide some example code, I can possibly give you a more useful answer.
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()
});
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);
The interviewer told me that I have to follow javascript "patterns" and write "clean code". He also said that I should follow the prototype pattern. Here is my code sample:
//namespace declrations
var MyNamespace = {};
MyNamespace.UCs = {};
MyNamespace.Pages = {};
//function declarations
MyNamespace.UCs.test = function () { alert('this is a test function in user control namespace.'); }
MyNamespace.Pages.test = function () { alert('this is a test function in web page namespace.'); }
So can anybody point me to why this code is not ok? I mean, I have declared namespaces first and then added my members and functions like the above sample. So does it really have issues or am I missing something?
Well when you are writing code in a large environment, lots of problems can start happening. So it's important to separate your class definitions from how you use those classes. Which also means you have to make classes that can be unit tested to prove that they do what you say they do. Javascript is not a true object orientated language and as such there are several ways to "fake" it. But because the language has a lot of flexibility, we can duplicate some approaches.
One thing we want to stay away from is something called function scope simply because it can cause unintended "features" later down the road when 3 or 4 other programmers start making assumptions about what your code is doing. If they don't know a global variable was overwritten one or two function closures ago, it will make finding that problem more difficult. So I would suggest using a small class created by John Resig as it provides a very simple approach that gives you alot of the functionality you need.
So let's write some code.
var myNamespace = myNamespace || { }
/**
* Used to store a single entry in the log
*
* #class
*/
var myNamespace.LogEntry = Class.extend({
/**
* Used to track the beginning of the page load
*
* #private
* #static
*/
PAGE_LOAD_TIME = new Date().getTime(),
/**
* Used to store the current time
*
* #type int
*/
time : undefined,
/**
* The message of this log entry
*
* #type string
*/
msg : undefined,
/**
* #constructor
*
* #param {string} msg The message of this log entry
*/
init : function (msg) {
this.time = new Date().getTime() - this.PAGE_LOAD_TIME;
this.msg = msg
},
/**
* Displays this log entry in a single string
*
* #return {string} String representation of this log entry
*/
toString : function () {
return this.time + ": " + this.msg;
}
});
/**
* Used to store a log entry that has data associated with it.
*
* #class
* #extends myNamespace.LogEntry
*/
var myNamespace.DataEntry = myNamespace.LogEntry.extend({
/**
* Used to store data associated with this log entry
*
* #type object
*/
data : undefined,
/**
* #constructor
*
* #param {string} msg The message that describes this log entry
* #param {object} data The data associated with this entry
*/
init : function (msg, data) {
this._super(msg);
this.data = data;
},
/**
* #return {string} The string representation of this log entry
*/
toString : function () {
// Uses a JSON library to stringify the data into a json string.
return this._super() + JSON.stringify(this.data);
}
});
/**
* Provides an interface to log messages
*
* #class
*/
var myNamespace.Log = Class.extend({
/**
* Stores log entries
*
* #type myNamespace.LogEntry[]
*/
log : undefined,
/**
* #constructor
*/
init : function () {
this.log = [ ];
},
/**
* Logs a message into the log
*
* #param {string} msg The message you want to log
*/
msg : function (msg) {
this.log.push(new myNamespace.LogEntry(msg));
},
/**
* Log a message and data into the log
*
* #param {string} msg The message of this log entry
* #param {object} data The data associated with this log entry
*/
data : function(msg, data) {
this.log.push(new myNamespace.DataEntry(msg, data));
}
});
Ok, there is lots of stuff going on here. The main part is that this is all definitions of classes. I don't actually use anything up there. The program only stores the current time in LogEntry.PAGE_LOAD_START which is declared #static so the behavior will be expected. I've used lots of jsDocs here to make everything clear about what the intentions are. A program like intelliJ can use those to give your code feedback if you aren't using the classes the way you've documentated them.
This program will let you create and store log entries with possibly logging data. There are alot of other ways to do this. I declare everything before the constructor instead of inside the constructor so that I can document the types and whether they are private.
A programmer that has to use the log will know exactly how to use it and if they want to create or extend these classes, they can do so without unintended effects from function closure.
Here's how to use it:
var anotherNamespace = anotherNamespace || {};
var anotherNamespace = new myNamespace.Log();
...
anotherNamespace.log.msg("This is a test");
...
anotherNamespace.log.data("Test msg with data", data);
Of course the obvious thing missing here is a way to display all the data. But that could be in another class that iterates through the Log.log array and spits out the toString() into a web page or file. The point here is that the classes and their functions are simple, unit testable, and definition only.
FIrst of all, make use of object literals, all those assignments are a waste lines.
Second, you don't have the prototype "pattern" implemented anywhere, also I'd go for some encapsulation if you have full control over the namespaces:
(function() { // anonymous wrapper
function Foo(a) { // i can haz prototype "Pattern"
this.memberVal = a;
}
Foo.prototype = {
someMethod: function(value) {
console.log(value, this.memberVal);
},
yam: function() {
}
};
// Let's assume we have full control here, otherwise Alnitak's answer is fine too.
window.namespace = {
test: {
Foo: Foo
}
};
})();
var test = new namespace.test.Foo(123);
test.someMethod('bla');
Maybe the interviewer literally meant you should be using the 'namespace' method?
Tutorial with example of usage:
http://elegantcode.com/2011/01/26/basic-javascript-part-8-namespaces/
Your code looks (mostly) fine, if the intent was to just put utility functions in a shared namespace, without any object orientation or data encapsulation.
I would have made one change, on the initial name space declarations:
var MyNamespace = MyNamespace || {};
MyNamespace.UCs = MyNamespace.UCs || {};
MyNamespace.Pages = Mynamespace.Pages || {};
to ensure that the code didn't obliterate any existing methods in those namespaces.