I am trying to automate a particular module in our JS library and am stuck at a point where I want to define a set of properties (lets say an object that goes as construction parameter of a class).
/**
* This function initiates world peace!
* #constructor
* #param {object} defaults - The options to initiate peace.
* #param {number} defaults.issues - The number of issues being taken up.
* #param {string} defaults.source - The name of the location where process starts.
*/
var WorldPeace = function (defaults) {
// code here
};
It is well and good had all properties of the construction was defined at one location. Unfortunately, my code has a number of modules contributing to that construction properties. Lets say, at some other portion of the code (in a later file) causes to have a couple of more properties
* #param {Date} defaults.start - The date when the process started.
* #param {Date} defaults.stop - The date when the process should stop.
How do I go about adding to the original set of properties that I had previously defined for WorldPeace function? Doing something like a mixin or subclassing the properties would be going overboard! As such, if I can simply inject to a property list definition it would be great.
The easiest method is to use a record type:
/**
* This function initiates world peace!
* #constructor
* #param {{issues: number, source: string}} defaults - options to initiate peace.
*/
var WorldPeace = function (defaults) {
// code here
};
You could also implement an interface:
/** #interface */
var WordPeaceDefaults;
/** #type {number} */
WorldPeaceDefaults.prototype.issues;
/** #type {string} */
WorldPeaceDefaults.prototype.source;
/**
* This function initiates world peace!
* #constructor
* #param {WorldPeaceDefaults} defaults - options to initiate peace.
*/
var WorldPeace = function (defaults) {
// code here
};
/**
* #constructor
* #implements {WorldPeaceDefaults}
*/
function MyWorldPeaceDefaults() {}
/** #type {number} */
MyWorldPeaceDefaults.prototype.issues = 0;
/** #type {string} */
MyWorldPeaceDefaults.prototype.source = '';
WordPeace(new MyWorldPeaceDefaults);
Another way to do it would be to use a typedef:
/**
* #typedef {{
* issues: number,
* source: string
* }}
*/
var WorldPeaceOptions;
/**
* #constructor
* #param {WorldPeaceOptions} defaults
*/
var WorldPeace = function (defaults) {
// code here
};
Related
So let's set the table here.
I have a class called Command and in that class I have an object as the parameter with 3 properties (name, description, execute())
class Command {
/**
*
* #param {Object} opts
* #param {String} opts.name
* #param {String} opts.description
* #param {Function} opts.execute
*/
constructor(opts) {
this.name = opts.name;
this.description = opts.description;
this.execute = opts.execute;
}
}
The execute property is a function and I want to pass in another set of JSDoc line for the parameter in those function.
It should generally work like this
module.exports = {
name: "Hello!",
description: "----",
/**
* #param {Message} message
* #param {Client} client
* #param {Array} args
*/
execute(message, client, args) {},
};
Is it possible to do this, or do I have to switch over to TypeScript?
Thank you in advance
you can use typedef and property, but you need to write it on all files you use it, it's better if you use a tsconfig.json.
you dont need typescript all you need is a tsconfig.json, but ofcourse typescript would be better.
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
/**
* The complete Triforce, or one or more components of the Triforce.
* #typedef {Object} WishGranter~Triforce
* #property {boolean} hasCourage - Indicates whether the Courage component is present.
* #property {boolean} hasPower - Indicates whether the Power component is present.
* #property {boolean} hasWisdom - Indicates whether the Wisdom component is present.
*/
/**
* A class for granting wishes, powered by the Triforce.
* #class
* #param {...WishGranter~Triforce} triforce - One to three {#link WishGranter~Triforce} objects
* containing all three components of the Triforce.
*/
function WishGranter(triforce) {}
I ask this mostly from a Angular perspective (but any advice would help). I have JSDoc's on my functions but it makes the code look very messy. I would just like to know if there is a way to move the JSDoc's to some type of external file.
An example of my JSDoc's:
/**
* Does a GET call on the service MyGetCall
* #param {string} pUserID - 1st Parameter: User Login ID
* #param {string} pPassword - 2nd Parameter: User Password
* #returns The Call's Http Observable (subscribe to this function).
* #example this.flowservice.MyGetCall('Johnny', 'MySuperSecretPassword')
* .subscribe(response => {
* console.log(response)
* });
*/
MyGetCall(pUserID: string, pPassword: string): Observable<any> {
const temp = this.httpclient.get<JSON>(`http://XXX/MyGetCall?userid=${pUserID}&password=${pPassword}`, {headers: this.headers});
return temp;
}
So in this example I would like to remove all the JSDoc's and put it in some kind of external file (jsdocs.xxx). This file would then look something like this:
MyGetCall:
/**
* Does a GET call on the service MyGetCall
* #param {string} pUserID - 1st Parameter: User Login ID
* #param {string} pPassword - 2nd Parameter: User Password
* #returns The Call's Http Observable (subscribe to this function).
* #example this.flowservice.MyGetCall('Johnny', 'MySuperSecretPassword')
* .subscribe(response => {
* console.log(response)
* });
*/
MyOtherFunction:
...
MyOtherOtherFunction:
...
Then I can import this file (jsdocs.xxx) somewhere for it to work with my app. For anyone that has used JSDoc's I hope this makes sense.
If, inline, I would document a class method like so:
/**
* #class
* #alias fileReader
*/
function fileReader() {
/**
* Tells the caller if it can handle the given file by returning a boolean.
*
* #param {File} file A `File` object.
* #returns {boolean} `true` if this reader can read a file.
*/
this.canRead = function (file) {
...
};
}
Instead, I could document my method somewhere else:
/**
* #class
* #alias fileReader
*/
function fileReader() {
this.canRead = function (file) {
...
};
}
And the documentation could be in a different file, like so:
/**
* Tells the caller if it can handle the given file by returning a boolean.
*
* #function canRead
* #memberof fileReader
* #instance
* #param {File} file A `File` object.
* #returns {boolean} `true` if this reader can read a file.
*/
The #function parameter defines the name of the function if the jsdoc isn't immediately followed by an actual function. The #memberof tells it the parent class or namespace. The #instance says that it is a method that requires an instantiated class rather.
For your example, I'm guessing that the documentation would be
/**
* Does a GET call on the service MyGetCall
* #function MyGetCall
* #memberof flowservice
* #instance
* #param {string} pUserID - 1st Parameter: User Login ID
* #param {string} pPassword - 2nd Parameter: User Password
* #returns The Call's Http Observable (subscribe to this function).
* #example this.flowservice.MyGetCall('Johnny', 'MySuperSecretPassword')
* .subscribe(response => {
* console.log(response)
* });
*/
This answer is mostly catered to VS Code if you're hoping to get Intellisense to pick up your types.
I've had success with simply creating my types in a typedefs.js file and referencing the type by using the ts/vscode import(path/to/file).Foo tag.
JSDoc does not support this syntax out of the box, so I suggest also using jsdoc-tsimport-plugin in order to parse your docs.
Eg: typedef.js:
/**
* #typedef {Object} Foo
* #property {string} id
*/
/**
* #typedef {Object} Bar
* #property {string[]} things
*/
// having to export an empty object here is annoying,
// but required for vscode to pass on your types.
export {};
coolFunction.js
/**
* This function is super dope
* #param {import('../typedef').Foo} foo - a foo
* #return {import('../typedef').Bar} bar - an array of bars
*/
export function (foo) {
// do cool things
return bar;
}
I defined a #callback that way :
/**
* #callback MyClass~Handler
* #param {string} target
* #param {Options} options - Original options
*/
I use it to define my class prototype :
/**
* #param {string} name - The name
* #param {MyClass~Handler} handler
* #private
*/
MyClass.prototype._builder = function(name, handler) { //...
But how to tell JSDoc that the following function has the same definition as my MyClass~Handler (something that would be like #isacallback in the following code)?
/**
* Default handler
* #isacallback {MyClass-Handler}
* #private
*/
MyClass.prototype._defaultHandler = function(target, options) { // ...
I haven't tested this, but since #callback just defines a type, couldn't you use the #type declaration?
In other words:
/**
* Default handler
* #type {MyClass-Handler}
* #private
*/
MyClass.prototype._defaultHandler = function(target, options) { // ...
See http://usejsdoc.org/tags-type.html for further info on #type.
I have a class named FileDownloader and I've tried documenting it, but the properties and method declared using prototype aren't generated in the output file.
As stated on the title, I use jsdoc 3.3.0-alpha5.
Here's the code:
/**
* #class
* #memberOf module:utils
*/
FileDownloader = function() {};
/**
* #type {Boolean}
*/
FileDownloader.prototype.overwrite = false;
/**
* #type {String}
*/
FileDownloader.prototype.dir = config.dealImagePath;
/**
* #param {String} url
* #param {Function} done
* #param {Object} done.err
* #param {String} done.file
*/
FileDownloader.prototype.download = function(url, done) {
//...
};
Here is the generated document:
new FileDownloader()
| Source: path/to/file.js
Any idea?
The reason is memberOf in FileDownloader description.
You should set module before, all symbols in the file are assumed to be members of the module. http://usejsdoc.org/tags-module.html
Like this
/** #module utils */
/**
* #class
*/
var FileDownloader = function() {};
/**
* #type {Boolean}
*/
FileDownloader.prototype.overwrite = false;
...
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);