Best way to document anonymous objects and functions with jsdoc - javascript

Edit: This is technically a 2 part question. I've chosen the best answer that covers the question in general and linked to the answer that handles the specific question.
What is the best way to document anonymous objects and functions with jsdoc?
/**
* #class {Page} Page Class specification
*/
var Page = function() {
/**
* Get a page from the server
* #param {PageRequest} pageRequest Info on the page you want to request
* #param {function} callback Function executed when page is retrieved
*/
this.getPage = function(pageRequest, callback) {
};
};
Neither the PageRequest object or the callback exist in code. They will be provided to getPage() at runtime. But I would like to be able to define what the object and function are.
I can get away with creating the PageRequest object to document that:
/**
* #namespace {PageRequest} Object specification
* #property {String} pageId ID of the page you want.
* #property {String} pageName Name of the page you want.
*/
var PageRequest = {
pageId : null,
pageName : null
};
And that's fine (though I'm open to better ways to do this).
What is the best way to document the callback function? I want to make it know in the document that, for example, the callback function is in the form of:
callback: function({PageResponse} pageResponse, {PageRequestStatus} pageRequestStatus)
Any ideas how to do this?

You can document stuff that doesnt exist in the code by using the #name tag.
/**
* Description of the function
* #name IDontReallyExist
* #function
* #param {String} someParameter Description
*/
/**
* The CallAgain method calls the provided function twice
* #param {IDontReallyExist} func The function to call twice
*/
exports.CallAgain = function(func) { func(); func(); }
Here is the #name tag documentation. You might find name paths useful too.

You can use #callback or #typedef.
/**
* #callback arrayCallback
* #param {object} element - Value of array element
* #param {number} index - Index of array element
* #param {Array} array - Array itself
*/
/**
* #param {arrayCallback} callback - function applied against elements
* #return {Array} with elements transformed by callback
*/
Array.prototype.map = function(callback) { ... }

To compliment studgeek's answer, I've provided an example that shows what JsDoc with Google Closure Compiler lets you do.
Note that the documented anonymous types get removed from the generated minified file and the compiler ensures valid objects are passed in (when possible). However, even if you don't use the compiler, it can help the next developer and tools like WebStorm (IntelliJ) understand it and give you code completion.
// This defines an named type that you don't need much besides its name in the code
// Look at the definition of Page#getPage which illustrates defining a type inline
/** #typedef { pageId : string, pageName : string, contents: string} */
var PageResponse;
/**
* #class {Page} Page Class specification
*/
var Page = function() {
/**
* Get a page from the server
* #param {PageRequest} pageRequest Info on the page you want to request
*
* The type for the second parameter for the function below is defined inline
*
* #param {function(PageResponse, {statusCode: number, statusMsg: string})} callback
* Function executed when page is retrieved
*/
this.getPage = function(pageRequest, callback) {
};
};

#link can add inline links to methods and classes.
/**
* Get a page from the server
* #param {PageRequest} pageRequest Info on the page you want to request
* #param {function} callback Function executed when page is retrieved<br />
* function({#link PageResponse} pageResponse,{#link PageRequestStatus} pageRequestStatus)
*/
this.getPage = function (pageRequest, callback) {
};
Not ideal, but it gets the job done.

The Google Closure Compiler Annotations has Type Expressions for this which includes the ability to indicate type for specific arguments, return type, and even this. Many libraries are looking at following the Google Closure Compiler Annotations, because they want to use it to shrink their code. So it's got some momentum. The downside is I don't see a way to give the description.
For providing the description perhaps the JSDoc Toolkit Parameters With Properties approach would work (look at the bottom of the page). It's what I am doing right now. The JSDoc Toolkit is prepping to start work on V3, so feedback there might be good.

You could use #see to link to another method within the same class. The method would never be used, it would just be there for documentation purposes.
/**
* #class {Page} Page Class specification
*/
var Page = function() {
/**
* Get a page from the server
* #param {PageRequest} pageRequest Info on the page you want to request
* #param {function} callback Function executed when page is retrieved
* #see #getPageCallback
*/
this.getPage = function (pageRequest, callback) {
};
/**
* Called when page request completes
* #param {PageResponse} pageResponse The requested page
* #param {PageRequestStatus} pageRequestStatus Status of the page
*/
//#ifdef 0
this.getPageCallback = function (pageResponse, pageRequestStatus) { };
//#endif
};
If you are using some kind of build system, the dummy method could easily be omitted from the build.

hope I'm not too late on this topic, but you can do something like this
/**
* Get a page from the server
* #param {PageRequest} pageRequest
* #param {(pageResponse: PageResponse, pageRequestStatus: PageRequestStatus) => void} callback
*/
this.getPage = function(pageRequest, callback) {}
the downside of this approach is that you can't document what each of the arguments does.

Related

IntelliSense/JSDoc #param = #return, a.k.a. how do I document wrapper functions?

I have a function which takes in another function as an argument, does something to that function-argument, and then returns that same function-argument (or at least returns a function with the exact same signature to it)
/**
* The param and return should be the same (as far as JSDoc is concerned)!
* #param {Function} fnToWrap
* #returns {fnToWrap}
*/
function wrapperFunction(fnToWrap) {
// Does something...
return fnToWrap;
}
However, as you can see from my comments in the following...
/**
* IntelliSense detects this...
* #param paramA
* #returns
*/
var arbitraryFn = function(paramA) {
return paramA * 2;
}
// But NOT this!!!
var wrappedArbitraryFn = wrapperFunction(arbitraryFn);
... IntelliSense will autocomplete when calling arbitraryFn() but not wrappedArbitraryFn().
Is there any way to get IntelliSense to dynamically autocomplete my wrapped functions with the same signature as their unwrapped counterparts, i.e. without having to explicitly re-document every newly-wrapped function?
I'm revisiting this again as I now have a (near-)perfect solution for my use-case. The reality is IntelliSense is far more TypeScript than it is standard JSDoc. As it turns out, you can leverage the #template tag to solve the above, and as far as I've seen, anything I can accomplish in TypeScript I can also accomplish in JavaScript IntelliSense:
The New Wrapper Function
/**
* The param and return should be the same (as far as JSDoc is concerned)!
* #template {Function} T
* #param {T} fnToWrap
* #returns {T}
*/
function wrapperFunction(fnToWrap) {
// Does something...
return fnToWrap;
}
Wrapping a Previously-Declared Function
/**
* IntelliSense detects this...
* #param {Number} paramA
*/
function previouslyDeclared(paramA) {
return paramA * 2;
}
// And it also detects this!
var afterWrapping = wrapperFunction(previouslyDeclared);
Wrapping an inline Function
// And it also detects this!
var wrappedArbitraryFn = wrapperFunction(
/**
* IntelliSense detects this
* #param {String} a
* #param {Number} b
* #param {Function} c
* #returns
*/
(a, b, c) => {
return 22;
}
);
Only downside for me is the inline is a bit ugly, imo, but it works. However, do note that this may not be a valid solution for true/vanilla JSDoc. This solution is great if, like me, you don't really use/care about JSDoc and you're really just here for the IntelliSense piece.
This isn't exactly what I'm looking for, but after posting this question I did discover this method which isn't too bad:
/**
* #type {arbitraryFn}
*/
var wrappedArbitraryFn = wrapperFunction(arbitraryFn);
This works and isn't too tedious, however be aware of the following:
This requires pre-defining the original function. In other words, I can't get this to work for functions declared inside the call to the wrapper function, e.g. this won't work for var wrapped = wrapperFunction((a, b) => a + b)
This will break if you try to rename the function with the "Rename Symbol" command (F2) in Visual Studio Code unless you document the original function with an #typedef. Putting a #typedef every time might be fine, but I don't know if there are any side effects to that tag. Plus it's an extra step to remember, and one that doesn't have obvious/immediate consequences if you forget it 😟
So I'm still very open to answers!
#user3781737 solution worked for me, after 6+ hours of debugging.
If anyone is having trouble getting props suggestions to work on components with forwarded props ie: {...props} I have found this solution using JSdoc #type annotation. It requires you have access to the props of the component where you are forwarding the props to:
import React from 'react';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { HeaderButton, HeaderButtonProps } from 'react-navigation-header-buttons';
import PropTypes from 'prop-types';
/**
* Used to wrap react-navigations default headerButton.
*
* #type {React.FC<ButtonPropTypes & HeaderButtonProps>}
*/
const DefaultHeaderButton = props => {
return (
<HeaderButton
{...props}
IconComponent={MaterialCommunityIcons}
iconSize={30}
color={props.color || "#fff"}
/>
);
};
DefaultHeaderButton.displayName = 'DefaultHeaderButton';
const ButtonPropTypes = {
color: PropTypes.string
}
DefaultHeaderButton.propTypes = ButtonPropTypes;
export default DefaultHeaderButton;
Edit: I realized this is only necessary if the propTypes are a typescript interface while using javascript. In most cases you can just spread the original item's propTypes into your custom propTypes.

jsdoc and vscode: Documenting a function passed as an argument to another function

I'm trying to document the input parameters to a function in javascript, but I can't work out how to do it in jsdoc.
I've looked at the jsdoc documentation which suggests that using the #callback comment is what's required, but Visual Studio Code (vscode) doesn't highlight it as per the screenshot.
The intellisense for the location parameter shows that it's type any rather than of type locator (a function with a parameter of id which returns a Location).
Example code which shows a function calling a function passed as a parameter:
class Location {
constructor(position, count) {
this.position = position;
this.count = count;
}
}
const items = {
'USB Cable': new Location('Desk Drawer', 123),
Keyboard: new Location('Desk Surface', 1),
};
/**
* A locater.
* #param {string} id
* #returns {Location}
*/
const locaterA = id => items[id];
/**
* Finds the item by its unique id.
* #callback locater
* #param {string} id
* #returns {Location}
*/
/**
* Attempt to find the item with the given locater.
* #param {string} id
* #param {locater} locater
*/
const locate = (id, locater) => locater(id);
const result = locate('USB Cable', locaterA);
console.log(result);
Is this a problem with what I'm doing, vsdoc not supporting the use case, or vscode not supporting it?
Edit: vscode seems to be supporting #callback starting from TypeScript 2.9
Vscode's IntelliSense doesn't support #callback. It's being tracked here: https://github.com/Microsoft/TypeScript/issues/7515.
As a convenient workaround you can use #typedef:
/**
* Finds the item by its unique id.
* #typedef {function(string): Location} Locater
*/
/**
* Attempt to find the item with the given locater.
* #param {string} id
* #param {Locater} locater
*/
const locate = (id, locater) => locater(id);
It looks like you're using it correctly, per JSDoc itself. However, it looks like Visual Studio may only support a limited subset of JSDoc, which doesn't include #callback: https://msdn.microsoft.com/en-us/library/mt162307.aspx
I don't have Visual Studio handy, but you might try the Google Closure style, which is to do it like this:
#param { function(string) : number } locator
This says that it's a function that takes a string, and returns a number.
You can find that documentation here: https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler (search for "Function Return Type" to jump to the relevant section).
I've noticed with JetBrains stuff at least, that it supports that syntax.
Since I was sent here from reddit concerning the documentation of an async function passed to another func, I answer here even if it's not exactly the op's question.
It's enough to document the return of the function with a Promise, in ES6 :
/**
* #param {(input: any) => Promise<any>} asyncFunctionAsParam - async function given
*/
const myfunc = (asyncFunctionAsParam) => {
// ...
}

How to use jsdoc for a meteor application

What is the correct way to use JSDoc within a meteor application?
Below there is my way to document the code, but there is no 'connection' between all parts.
Everything belongs to the example template, so the output of jsdoc should be structured correctly.
How can I improve this documentation?
Template.example.helpers({
/**
* Get all categories
* #name categories
* #return {Cursor}
* #summary Cursor categories
*/
categories() {
return Categories.find({});
},
});
Template.example.onCreated(
/**
* If template is created (still not rendered), ReactiveDict variable is initialized
* #function
* #name onCreated
* #summary Set ReactiveDict
*/
function() {
this.something = new ReactiveDict();
}
);
Template.example.events({
/**
* Clicking on category will show a console message
* #event
* #summary Console message
*/
'click #category': (event, template) => {
console.log('nice')
}
});
Where I work, we encountered the same situation a few months ago, what we concluded was that jsdoc was just not adapted to auto-doc with Meteor's implementation. We ended up using https://github.com/fabienb4/meteor-jsdoc which gives us full satisfaction.
It basically extends jsdoc syntax with meteor specific keywords so you can specify what is a Meteor.call, what is a collection helper and so on. Once configured and running, the output is basically what Meteor's documentation used to look like before 1.3 (as the author says, it's "Based on the templates from Meteor own docs.").
Edit: As we don't use Meteor's templating system, I don't have an existing example but I adapted a collection helper to your case, tell me if there is anything unclear. The trick is to play with #memberOf, #isMethod, etc depending on how you want your doc to be displayed.
/**
* #memberOf Foo Template
* #summary Returns Bar conversation attached to stuff
* #param {String} fooId
* #param {Boolean} forced # Use to create foo when it's not found
* #returns {Object} Bar
*/
getStuff: function(fooId, forced=false) {
'use strict';
/** your code **/
}

JSDoc #param together with #deprecated

I have a JavaScript function getting some parameters including object types. However, one property of a parameter, which is an object, will be used as deprecated. I would like to indicate this situation in the documentation, however I don't know how to use #param tag with #deprecated. Consider the example below:
/**
* This function does something.
*
* #name myFunction
* #function
* #since 3.0
* #param {function} [onSuccess] success callback
* #param {function} [onFailure] failure callback
* #param {object} [options] options for function
* #param {string} [options.lang] display language
* #param {string} [options.type] type of sth
*/
this.myFunction= function (onSuccess, onFailure, options) {
//do something
}
I want to deprecate "type" property of "options" object. How can I do that, or can I?
Official JSDoc documentation does not indicate that the #deprecated tag can be used for deprecating anything else than an entire symbol.
The #deprecated tag can be used to document that for example a function as a whole has been deprecated.
/**
* #deprecated since version 2.0.0
*/
function old () {
}
You can of course, as #Droogans said in the comments, add something like deprecated: in front of the #param description. If a developer somehow still ends up using the deprecated feature, you could implement a warning of some sorts.
/**
* #param {string=} bar - Deprecated: description
*/
function foo (bar) {
if (bar) {
console.warn('Parameter bar has been deprecated since 2.0.0')
}
}
A suggestion is using typescript, like so:
function test(
options: {
/**
* #deprecated use newName instead
*/
name?: string,
newName?: string
}) {
}

Annotating a class received from JSON server call

I am trying to properly annotate all my Javascript code and am still a beginner in this field.
I get objects from a server call that are directly in json. Those objects are passed to functions and I call members directly. It obviously is very error prone while obfuscating so I am trying to annotate everything properly.
My understanding is that (i) I should create a class for this object even though it is never created directly with new and (ii) I should be careful with member names since they are fixed by the server response and should not be changed (or should be aliased beforehand).
I have two questions: are my asumptions correct ? how to do the annotation based on that.
Here is a sample code to illustrate
$.get("url.php", function(data) {
process(data);}, "json"); // <= returns {"a":"abc","b":1}
function process(data) {
$("#someElement").html(data.a + data.b);
}
Here is the class I was planning on creating for closure compilation purposes
/**
* The object that is sent back from the server
* #constructor
*/
function ServerResponse() {
/** #type {string} a */
this["a"] = "";
/** #type {number} b */
this["b"] = 0;
}
Here is the annotation for my process function
/**
* Process data from the server
* #param {ServerResponse} data: the Object sent back from the server
*/
Is my annotation correct? Is it robust to obfuscation?
Thanks a lot for your help
If you quote property names, you need to quote them consistently at every usage. If you haven't see this, you should read:
https://developers.google.com/closure/compiler/docs/api-tutorial3
So that your first snippet would be:
function process(data) {
$("#someElement").html(data['a'] + data['b']);
}
If you are trying to avoid quoting or if you would like the compiler to type check the usage of the properties (quoted properties references are not checked), you should define an extern file to include with the compilation of your source:
/**
* #fileoverview
* #externs
*/
/** #interface */
function ServerResponse() {}
/** #type {string} */
ServerResponse.prototype.a;
/** #type {number} */
ServerResponse.prototype.b;

Categories

Resources