Enum as #param type in JSDoc - javascript

Is it possible to use an enum for the JSDoc #param type declaration like in the following example?
/**
* #enum { Number }
*/
const TYPES = {
TYPE_A: 1,
TYPE_B: 2
}
/**
* #param { TYPES } type
*/
function useTypesEnum( type ) {
}
If I use an IDE like Eclipse etc. for JavaScript, there should no warning be raised?

You can achieve that, by doing this:
/**
* #param {(1|2)} type
*/
function useTypesEnum(type) {
}

So it seems this is the right way to document everything without any warning
/**
* #typedef {number} MyType
**/
/**
* #enum {MyType}
*/
var TYPES = {
TYPE_A: 1,
TYPE_B: 2
}
/**
* #param {MyType} type
*/
function useTypesEnum( type ) {
}
This means:
MyType is a number
TYPES is an enum that holds MyType values
This function accepts enums that output MyType values
Works for me on intellij 2017.1
However - this will still allow each string to be passed to the function without warnings.
If you want to specify the enum values too - so it should raise errors if another string was used, use the method described at: https://stackoverflow.com/a/36501659/1068746
/**
* #typedef FieldType
* #property {string} Text "text"
* #property {string} Date "date"
* #property {string} DateTime "datetime"
* #property {string} Number "number"
* #property {string} Currency "currency"
* #property {string} CheckBox "checkbox"
* #property {string} ComboBox "combobox"
* #property {string} Dropdownlist "dropdownlist"
* #property {string} Label "label"
* #property {string} TextArea "textarea"
* #property {string} JsonEditor "jsoneditor"
* #property {string} NoteEditor "noteeditor"
* #property {string} ScriptEditor "scripteditor"
* #property {string} SqlEditor "sqleditor"
*/

I use the following:
const TYPES = {
0: "TYPE_A",
1: "TYPE_B"
}
/**
* #param {keyof TYPES} type
*/
function useTypesEnum(type) {
// ...
}
This shows the correct values as a suggestion in VSCode. It is nicely readable, gives developers a clue about which value represents what and the enum values can be used in runtime.
If I do not need the values of TYPES in runtime, I even prefer to use the TYPES as a #typedef:
/**
* #typedef {{
* 0: "TYPE_A",
* 1: "TYPE_B"
* }} TYPES
*/
/**
* #param {keyof TYPES} type
*/
function useTypesEnum(type) {
// ...
}
If the value of the enum should be used, or the enum keys and values must be flipped for any reason, I use the valueOf<T> helper. The downside is, that this does not offer auto-completion in VSCode. But at least the functions parameter definition is, to some extent, readable.
/**
* #typedef {T[keyof T]} valueOf<T>
* #template T
*/
const TYPES = {
"TYPE_A": 0,
"TYPE_B": 1
};
/**
* #param {valueOf<TYPES>} type
*/
function useTypesEnum(type) {
// ...
}

Unfortunately, the only way I found is to define another type (#typedef):
/**
* #enum { Number }
*/
const TYPES = {
TYPE_A: 1,
TYPE_B: 2
}
/** #typedef {'TYPE_A'|'TYPE_B'} TYPES_KEYS */
/**
* #param { TYPES_KEYS } input
*/
function useTypesEnum(input) {
// ...
}

JSDoc comments have no impact on JavaScript code. What it does influence is some tools designed to use that information. Two of the tools that work with JSDoc comments are the documentation generator and the Google Closure Compiler.
I am not particularly familiar with JSDoc 3, in which the #enum tag has been added, but I would assume it works just as any other type.
The Closure Compiler also recognizes the enum correctly and you can use it just like you mentioned in the example and get all the benefits of the compiler (ex: type checking).

Related

Merge two typedef files jsdoc

I have two separate typedef files.
typedefFirst.js
/**
* ControllerResponse
* #typedef {Object} ControllerResponse
* #property {Number} statusCode
* #property {{ data: (Object | Array )}} body
*/
/**
* ExpressRouter
* #typedef {import('express').Router} ExpressRouter
*/
typedefSecond.js
/**
* Context
* #typedef {Object} Context
* #property {String} userId
* #property {String} role
* #property {String} accessToken
*/
/**
* ExpressRequest
* #typedef {import('express').Request} ExpressRequest
*/
I want to aggregate all types and define all the types in one file i.e. typedefFirst.js.
I tried export.unused = {} in typedefSecond.js and tried below in typedefFirst.js,
/**
* #typedef {import('./typedefSecond.js')}
*/
But it is not working.
How can I merge both typedef files?

JSDOC with import/export syntax (ES modules)

How does one specify exported (public) properties of a module with JSDoc when using import and export?
My module structure is
/lib
|- src/
| |- func-a.js
| |- func-b.js
|- index.js
lib/src/func-a.js
/** Module for queue A
* #exports funcA/funcA
*/
/**
* Processes A queue
* #public
* #param {number} n - input number
* #returns {number} result - output number
*/
const funcA = (n) => {
return addTwoToN(n)
}
/**
* Adds integer two to input
* #private
* #param {number} n - input number
* #returns {number} addition - input plus two
*/
const addTwoToN = (n) => n + 2
export default funcA
lib/src/func-b.js
/**
* Processes B queue
* #public
* #param {number} n - input number
* #returns {number} result - output number
*/
const funcB = (n) => {
return subtractTwoFromN(n)
}
/**
* #private
* #param {number} n - input number
* #returns {number} subtraction - input minus two
*/
const subtractTwoFromN = (n) => n - 2
export default funcB
And finally lib/index.js
import funcA from './src/func-a';
import funcB from './src/func-b';
/**
* Func module
* #module func
*/
export {
/**
* Queue func A
* #module func/funcA
*/
funcA,
/**
* Queue func B
* #module func/funcB
*/
funcB,
}
Now if I create some other Javascript file and do import { funcA, funcB } from 'lib/, it seems to pick up annotation in the editor and I'm getting intellisense which is nice.
Where I'm stuck is when trying to generate documentation via jsdoc CLI like so:
jsdoc lib/index.js --destination docs/
I end up with this:
Which is incomplete, there's no funcB mentioned. When opening funcA link in sidebar, I only get this:
There is no API documentation for that part of the library.
I am probably approaching this in a wrong way. Can I even use multiple #module annotations? Or should I be using #typedef instead?
I expected the documentation to basically show structure of the lib/ and display the annotation for items marked with #public but maybe my expectation is totally different.

JSDoc Mark something as Class after instantiation and define constructor properties

I have mongoose create a Model(class) for me, and I would like to have the class to have intellisense. The problem is I don't know how to mark something as a class, and how to type the constructor for that class.
Looking at JSDocs' docs they only specify how to type a class at the declaration, not when it already has been instantiated. The #class and #constructor tags don't seem to do anything.
Right now I am getting my intellisense by marking it as a function (which it is under the hood, but it would still be great to have the correct colors in VSC) and defining the params:
/**
* #typedef QPE_Opts
* #type {object}
* #prop {string} id - Id of the user
* ...
*/
/**
* #type {function(QPE_Opts) : mongoose.Model<mongoose.Document, {}> }}
*/
const Queue_PoolEntry = mongoose.model('Queue_PoolEntry', Queue_PoolEntrySchema);
Solution
The solution (Was looking at the JsDocs' Documentation which was not providing enough info, thanks #Graham P Heath for giving the better docs) :
/**
* #type {function(new:mongoose.Model<mongoose.Document, {}>, QPE_Opts ) }}
*/
If you are sure of the type of an object you can type cast it:
const Queue_PoolEntry = /** #type {QPE_Opts} */ (mongoose.model('Queue_PoolEntry',
Queue_PoolEntrySchema));
In situations where you can't actually be sure of the returned type casting may be a code smell. In that case: use code to determine the validity of your type assertion by checking the object for expected properties, and only then casting it.
let Queue_PoolEntry = mongoose.model('Queue_PoolEntry',
Queue_PoolEntrySchema);
if (Queue_PoolEntry && Queue_PoolEntry.id) {
Queue_PoolEntry = /** #type {QPE_Opts} */ (Queue_PoolEntry)
} else {
throw new Error('mongoose.model(\'Queue_PoolEntry\', Queue_PoolEntrySchema)'+
'returned something other than a QPE_Opts' + Queue_PoolEntry);
}

What should I change in my code to set 5 ZIP code digits instead of 6?

So this is my code which I've written with help from others because I don't understand that validating concept very well.
(function($) {
$.fn.bootstrapValidator.validators.usZipCode = {
/**
* Return true if and only if the input value is a valid US zip code
*
* #param {BootstrapValidator} validator The validator plugin instance
* #param {jQuery} $field Field element
* #param {Object} options
* #returns {boolean}
*/
validate: function(validateInstance, $field, options) {
var value = $field.val();
return /^\d{5}([\-]\d{4})?$/.test(value);
}
};
}(window.jQuery));

Document generic type parameters in JSDOC

In JSDoc there exists the possibility to document the exact types of array contents like this:
/** #param {Array.<MyClass>} myClasses An array of MyClass objects. */
TestClass.protoype.someMethod = function( myClasses ){
myClasses[0].aMethodOnMyClass();
}
This makes code completion in IDEs like WebStorm actually provide the right type information after the [0].. This works well for the Array type, however I have my own collection types where I would like to make use of this feature, too. The problem is I cannot find the right syntax (maybe because there is none, yet). I would love to be able to declare my class somehow like this:
/**
* #typeparam {T} the type parameter
* #constructor {Test2.<T>}
* */
Test2 = function(){};
/**
* #returns {T} a value of type T, where T is the generic type parameter of Test2
*/
Test2.prototype.getGenericValue = function(){}
This syntax or feature does not work with my IDE and is not listed here, so I am wondering whether there is a syntax for this use-case, either for WebStorm or any other JS authoring tool.
You can try using #template tag (undocumented tag used in Google Closure library - extremely limited form of generics). Something like:
/**
* Search an array for the first element that satisfies a given condition and
* return that element.
* #param {Array.<T>|goog.array.ArrayLike} arr Array or array
* like object over which to iterate.
* #param {?function(this:S, T, number, ?) : boolean} f The function to call
* for every element. This function takes 3 arguments (the element, the
* index and the array) and should return a boolean.
* #param {S=} opt_obj An optional "this" context for the function.
* #return {T} The first array element that passes the test, or null if no
* element is found.
* #template T,S
*/
goog.array.find = function(arr, f, opt_obj) {
var i = goog.array.findIndex(arr, f, opt_obj);
return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
};
WebStorm uses this tag for type hinting - i.e. if we pass array of strings to goog.array.find in the sample above , IDE will know that return type is string, so string completion options will be suggested etc.
Not sure if this is what you are looking for... The post that looks related is here.
In the meantime, support for this feature has been finalized and is now documented on the Closure Compiler JSDOC page for generics.
Basically it works like this for ES6 classes:
/** #template T */
class Foo {
/** #return {T} */
get() { ... };
/** #param {T} t */
set(t) { ... };
}
... and like this for pre-ES6 code:
/**
* #constructor
* #template T
*/
Foo = function() { ... };
and
/** #return {T} */
Foo.prototype.get = function() { ... };
/** #param {T} t */
Foo.prototype.set = function(t) { ... };
WebStorm 7.0 did not support this feature at the time the original answer was written, but as of today (2019) all JetBrains IDEs understand this syntax, properly.
The following code works fine for me in WebStorm 8.
/** #type {Array.<MyPair.<Event, Array.<Thought>>>} */
scope.pairs = [];
/**
* #template TFirst, TSecond
*/
function MyPair(first, second){
this.first = first;
this.second = second;
}
/** #type {TFirst} */
MyPair.prototype.first = null;
/** #type {TSecond} */
MyPair.prototype.second = null;
...
function Event(){}
...
...
function Thought(){}
...

Categories