How to share methods documentation within class with jsdoc? - javascript

My class has methods with same arguments description.
/**
* #constructor
*/
var Person = function (){};
Person.prototype = {
/**
* #param {!string} [arg] - words
*/
whisper: function(arg){
console.log(arg.toLowerCase());
},
/**
* #param {!string} [arg] - words
*/
shout: function(arg){
console.log(arg.toUpperCase());
},
};
How to share method documentation within class?
Is it possible to not only share methods documentation, but mix it with inline method description? Example: each method will have different #return type.

Related

Type information for a conditional property in JSDoc

I'm adding type information for TypeScript to an existing JavaScript library via JSDoc. I have a constructor function that might set a property based on the value(s) of the parameter(s) it's given:
/**
* Settings you can use to configure an instance of an {#link ExampleClass}
*
* #typedef {Object} ExampleOptions
* #property {true} [option] You can set this to `true` to make a property on an instance of {#link ExampleClass} exist. If you don't set it, that property won't exist
*/
/**
* A thing that does stuff
*
* #constructor
* #param {ExampleOptions} [param] Options you can use to configure the new instance
*/
function ExampleClass(param) {
if(param.option === true) {
/**
* A property that only exists based on the options given to this constructor
*
* #type {boolean}
*/
this.property = true;
}
}
I was hoping that TypeScript would externally interpret the declaration of property to be like property?: boolean;, but it looks like it gets interpreted to be non-optional, and comes up in the autocomplete in my editor without having to check for its existence ahead of time. Ideally, I'd like for it to be an optional property that you'd have to check for before you can use it, or even allow you to use it unchecked if TypeScript can somehow guarantee that you had passed {option: true} to the constructor. How can I make that work?
Pre-declare the class with a #typedef with the optional properties and set it to the constructor as a variable of type {new () => ExampleClass}. This way it's even possible to declare this as the defined class and have code completion inside the constructor function itself. Something like the below:
/**
* Settings you can use to configure an instance of an {#link ExampleClass}
*
* #typedef {Object} ExampleOptions
* #property {true} [option] You can set this to `true` to make a property on an instance of {#link ExampleClass} exist. If you don't set it, that property won't exist
*/
/**
* #typedef {object} ExampleClass
* #prop {boolean} [property] A property that only exists based on the options given to this constructor
*/
/**
* A thing that does stuff
*
* #constructor
* #param {ExampleOptions} [param] Options you can use to configure the new instance
* #this {ExampleClass}
* #type {{new () => ExampleClass}}
*/
const ExampleClass = function (param) {
if(param.option === true) {
this.property = true;
}
}
Can you not just do
interface options {
option? : boolean;
}
/**
* A thing that does stuff
*
* #constructor
* #param {options} [param] Options you can use to configure the new instance
*/
function ExampleClass(param : options) {
if(param.option === true) {
/**
* A property that only exists based on the options given to this constructor
*
* #type {boolean}
*/
this.property = true;
}
}

Simultaneous use and sharing of public methods - right approach?

As title says, lets say that I have a class with many methods and 80% of those:
I need to establish also as a public (1:1 functionality)
They use each other
What approach is the best in terms of extending and testing in the future? Of course below examples are trivial, but lets assume that I got class with 20-30 methods inside. If it help, the project is written in ReactJS.
Approach 1:
This approach is the fastest for me.
No code repetition
I am utilizing functions inside class, also sharing outside
Some functions depends on other
class Approach1 {
names = [];
/* Establish methods for users also */
publicApi = {
check: this.check,
setValue: this.setValue
};
/**
* Public method 1
* #public
* #param {string} name
* #returns {boolean}
*/
check = name => name === "foo";
/**
* Public method 2 using internally method 1
* #public
* #param {string} name
* #returns {boolean}
*/
setValue = name => {
if (this.check(name)) names.push(name);
};
}
Approach 2:
I don't think this approach is good.
Code repetition
I am utilizing functions inside class, also sharing outside
Functions not depending on each other
class Approach2 {
names = [];
/* Establish methods for users also */
publicApi = {
check: this.check,
setValue: this.setValue
};
/**
* Public method 1
* #public
* #param {string} name
* #returns {boolean}
*/
check = name => name === "foo";
/**
* Public method 2 with own implementation
* #public
* #param {string} name
* #returns {boolean}
*/
setValue = name => {
if (name === "foo") names.push(name);
};
}
Approach 3:
This approach looks better, but I see a lot of additional code.
Additional code (I need to encapsulate almost every private method)
I am separating same functions to private and public
Private method can depends on each other
Public methods always depends on private methods
class Approach3 {
names = [];
/* Establish methods for users also */
publicApi = {
check: this.check,
setValue: this.setValue
};
/**
* Private method 1
* #private
* #param {string} name
* #returns {boolean}
*/
_check = name => name === "foo";
/**
* Private method 2
* #private
* #param {string} name
*/
_setValue = name => {
if (this._check(name)) names.push(name);
};
/**
* Public method 1 (encapsulating private method)
* #public
* #param {string} name
* #returns {boolean}
*/
check = name => _check(name);
/**
* Public method 2 (encapsulating private methods 1 and 2)
* #public
* #param {string} name
*/
setValue = name => {
if (this._check(name)) this._setValue(name);
};
}
Thank you for any tips :)
Approach 1 looks totally fine. What you need to think first of all - is public contract. Whether some public function use another public inside or not - not important. As you still need to cover with tests each piece of public API.
p.s. having 20-30 public methods inside one class - is a good time to rethink the design and split class to multiple classes/modules to comply with Single Responsibility Principle:)

JSDoc: Annotating an exported method

If I define an object like this:
/**
* My Cool Object
* #constructor
*/
function MyCoolObject() {
/**
* Cool Method, private
* #param {!string} parameter
*/
function localMethod(parameter) {
// do stuff
}
// Export the method
this.exportedMethod = localMethod;
}
I'd like to know, if at all possible, how to tell JSDOC to use the annotation for localMethod in exportedMethod, or how can I annotate exportedMethod, because if I do:
// Export the method
/**
* Cool Method
* #param {!string} parameter
*/
this.exportedMethod = localMethod;
JSDOC assumes it's a field rather than a method, then only uses the description, ignoring the #param part.
I would reduce it to:
/**
* My Cool Object
* #constructor
*/
function MyCoolObject() {
/**
* Cool Method, private
* #param {!string} parameter
*/
this.exportedMethod = function (parameter) {
// do stuff
};
}
You can do var localMethod = this.exportedMethod right after if you want a local reference to the method. In the off chance that you've over-simplified your example and you need to first assign to localMethod before assigning to this.exportedMethod you could do this:
/**
* My Cool Object
* #constructor
*/
function MyCoolObject() {
function localMethod(parameter) {
// do stuff
}
/**
* Cool Method, private
* #param {!string} parameter
* #function
*/
// Export the method
this.exportedMethod = localMethod;
}
The #function declaration tells jsdoc that it is dealing with a function.

Documenting the return of a javascript constructor with jsdoc

I have a javascript function that returns a constructor (see code sample below). How would I document this with the #returns tag of jsdoc. It doesnt seem correct to do #returns {MyConstructor} because that implies I am returning an instance of "MyConstructor" rather than the constructor itself, right?
function MyConstructor() {
var self = this;
self.myFunction = function() {
return true;
};
self.getMyFunctionResult = function() {
return self.myFunction();
};
}
/**
* #returns {?} A constructor that will be instantiated
*/
function getConstructor() {
return MyConstructor;
}
var constructor = getConstructor();
var instance = new constructor();
I do not think there is a way to use the brackets after #returns to document returning a specific instance. What goes in the brackets is interpreted as a type, always. This being said, there's a way to document that a specific instance of a type is being returned, by documenting the instance and using a link to the instance. I've shortened the code in the question to the essentials necessary to illustrate:
/**
* #class
*/
function MyConstructor() {
}
/**
* #returns {Function} A constructor that will be instantiated. Always
* returns {#link MyConstructor}.
*/
function getConstructor() {
return MyConstructor;
}
It can also be done with other things than classes:
/**
* #public
*/
var foo = 1;
/**
* #returns {number} {#link foo}.
*/
function getFoo(){
return foo;
}
As far as I know, this is as good as it gets with jsdoc 3.
Maybe little bit late, but I have problem to find proper answer for your question event today.
When I try generate JSDoc automatically on WebStorm, this is what I get:
class Test {}
/**
*
* #return {Test}
* #constructor
*/
function getTestConstructor() {
return Test;
}
Return type definition is still strange, but constructor annotation may fulfill the purpose.
You can check the types returned by your functions using:
console.log(typeof constructor, typeof instance); // function object
In the documentation it says:
/**
* Returns the sum of a and b
* #param {Number} a
* #param {Number} b
* #returns {Number} Sum of a and b
*/
function sum(a, b) {
return a + b;
}
http://usejsdoc.org/tags-returns.html
So in you example it would be:
/**
* Returns the MyConstructor class
* #returns {Function} MyConstructor class
*/
function getConstructor() {
return MyConstructor;
}
Or if you are creating an instance of an Item:
/**
* Returns an instance of the MyConstructor class
* #returns {Object} MyConstructor instance
*/
function getInstance() {
return new MyConstructor();
}

How do I JSDoc A Nested Object's Methods?

I've been trying to use JSDoc3 to generate documentation on a file, but I'm having some difficulty. The file (which is a Require.js module) basically looks like this:
define([], function() {
/*
* #exports mystuff/foo
*/
var foo = {
/**
* #member
*/
bar: {
/**
* #method
*/
baz: function() { /*...*/ }
}
};
return foo;
}
The problem is, I can't get baz to show up in the generated documentation. Instead I just get a documentation file for a foo/foo module, which lists a bar member, but bar has no baz (just a link to foo's source code).
I've tried changing bar's directive to #property instead, and I've tried changing baz's directive to #member or #property, but none of that helps. No matter what I do, baz just doesn't seem to want to show up.
Does anyone know what directive structure I could use to get baz to appear in the generated documentation?
P.S. I've tried reading pages like this one on the JSDoc site http://usejsdoc.org/howto-commonjs-modules.html, but it only describes cases of foo.bar, not foo.bar.baz.
You can use a combination of #module or #namespace along with #memberof.
define([], function() {
/**
* A test module foo
* #version 1.0
* #exports mystuff/foo
* #namespace foo
*/
var foo = {
/**
* A method in first level, just for test
* #memberof foo
* #method testFirstLvl
*/
testFirstLvl: function(msg) {},
/**
* Test child object with child namespace
* #memberof foo
* #type {object}
* #namespace foo.bar
*/
bar: {
/**
* A Test Inner method in child namespace
* #memberof foo.bar
* #method baz
*/
baz: function() { /*...*/ }
},
/**
* Test child object without namespace
* #memberof foo
* #type {object}
* #property {method} baz2 A child method as property defination
*/
bar2: {
/**
* A Test Inner method
* #memberof foo.bar2
* #method baz2
*/
baz2: function() { /*...*/ }
},
/**
* Test child object with namespace and property def.
* #memberof foo
* #type {object}
* #namespace foo.bar3
* #property {method} baz3 A child method as property defination
*/
bar3: {
/**
* A Test Inner method in child namespace
* #memberof foo.bar3
* #method baz3
*/
baz3: function() { /*...*/ }
},
/**
* Test child object
* #memberof foo
* #type {object}
* #property {method} baz4 A child method
*/
bar4: {
/**
* The #alias and #memberof! tags force JSDoc to document the
* property as `bar4.baz4` (rather than `baz4`) and to be a member of
* `Data#`. You can link to the property as {#link foo#bar4.baz4}.
* #alias bar4.baz4
* #memberof! foo#
* #method bar4.baz4
*/
baz4: function() { /*...*/ }
}
};
return foo;
});
EDIT as per Comment: (Single page solution for module)
bar4 without that ugly property table. ie #property removed from bar4.
define([], function() {
/**
* A test module foo
* #version 1.0
* #exports mystuff/foo
* #namespace foo
*/
var foo = {
/**
* A method in first level, just for test
* #memberof foo
* #method testFirstLvl
*/
testFirstLvl: function(msg) {},
/**
* Test child object
* #memberof foo
* #type {object}
*/
bar4: {
/**
* The #alias and #memberof! tags force JSDoc to document the
* property as `bar4.baz4` (rather than `baz4`) and to be a member of
* `Data#`. You can link to the property as {#link foo#bar4.baz4}.
* #alias bar4.baz4
* #memberof! foo#
* #method bar4.baz4
*/
baz4: function() { /*...*/ },
/**
* #memberof! for a memeber
* #alias bar4.test
* #memberof! foo#
* #member bar4.test
*/
test : true
}
};
return foo;
});
References -
Another Question about nested namespaces
For alternative way of using Namespaces
Documenting literal objects
*Note I haven't tried it myself. Please try and share the results.
Here's a simple way to do it:
/**
* #module mystuff/foo
* #version 1.0
*/
define([], function() {
/** #lends module:mystuff/foo */
var foo = {
/**
* A method in first level, just for test
*/
testFirstLvl: function(msg) {},
/**
* #namespace
*/
bar4: {
/**
* This is the description for baz4.
*/
baz4: function() { /*...*/ },
/**
* This is the description for test.
*/
test : true
}
};
return foo;
});
Note that jsdoc can infer the types baz4.baz4 and test without having to say #method and #member.
As far as having jsdoc3 put documentation for classes and namespaces on the same page as the module that defines them, I don't know how to do it.
I've been using jsdoc3 for months, documenting a small library and a large application with it. I prefer to bend to jsdoc3's will in some areas than have to type reams of #-directives to bend it to my will.
You can't document nested functions directly. I didn't like Prongs solution, so I used a different implementation without namespaces (it's JS, not Java!).
Update:
I updated my answer to reflect the exact use case given by the OP (which is fair, since JSdoc is pretty painful to use). Here is how it would work:
/** #module foobar */
/** #function */
function foobarbaz() {
/*
* You can't document properties inside a function as members, like you
* can for classes. In Javascript, functions are first-class objects. The
* workaround is to make it a #memberof it's closest parent (the module).
* manually linking it to the function using (see: {#link ...}), and giving
* it a #name.
*/
/**
* Foo object (see: {#link module:foobar~foobarbaz})
* #name foo
* #inner
* #private
* #memberof module:foobar
* #property {Object} foo - The foo object
* #property {Object} foo.bar - The bar object
* #property {function} foo.bar.baz - The baz function
*/
var foo = {
/*
* You can follow the same steps that was done for foo, with bar. Or if the
* #property description of foo.bar is enough, leave this alone.
*/
bar: {
/*
* Like the limitation with the foo object, you can only document members
* of #classes. Here I used the same technique as foo, except with baz.
*/
/**
* Baz function (see: {#link module:foobar~foo})
* #function
* #memberof module:foobar
* #returns {string} Some string
*/
baz: function() { /*...*/ }
}
};
return foo;
}
Unfortunately JSdoc is a port of Java, so it has a lot of features that make sense for Java but not for JS, and vice-versa. For example, since in JS functions are first-class objects, they can be treated as objects or functions. So doing something like this should work:
/** #function */
function hello() {
/** #member {Object} */
var hi = {};
}
But it won't, because JSdoc recognizes it as a function. You would have to use namespaces, my technique with #link, or to make it a class:
/** #class */
function Hello() {
/** #member {Object} */
var hi = {};
}
But then that doesn't make sense either. Do classes exist in JS? no, they don't.
I think we really need to find a better documentation solution. I've even seen inconsistencies in the documentation for with how types should be displayed (e.g. {object} vs {Object}).
You can also use my technique to document closures.
Just to improve on Prongs's answer a bit for JSDoc3, I was only able to get it to work when I used the #instance annotation in lieu of #member.
ES6 example code follows:
class Test
{
/**
* #param {object} something
*/
constructor(something)
{
this.somethingElse = something;
/**
* This sub-object contains all sub-class functionality.
*
* #type {object}
*/
this.topology = {
/**
* Informative comment here!
*
* #alias topology.toJSON
* #memberof! Test#
* #instance topology.toJSON
*
* #returns {object} JSON object
*/
toJSON()
{
return deepclone(privatesMap.get(this).innerJSON);
},
...
}
}
}

Categories

Resources