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);
}
Related
I'm centralizing user-facing message string templates in a Node API I'm writing so all my strings live together. I'd like to be able to build these strings dynamically from elsewhere in the code by passing a template function the arguments it needs to build the message, but the problem is that I can't figure out how to structure JSDoc to perform autocompletion for the object that's passed to my "string builders", AKA the functions returned from template below. See below for an example of what this would ideally look like. You can mostly ignore the template function implementation as it's taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates.
// strings.js
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
function template(strings, ...keys) {
return function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
};
}
/**
* Returns a string for welcoming mentees to the platform.
* #param {object} params
* #param {string} params.name The name of the mentee.
*/
export const menteeOnboardingWelcome = template`Hi ${'name'}, welcome!`;
/**
* Returns a string for when a mentee and mentor are matched.
* #param {object} params
* #param {string} params.menteeName The name of the mentee.
* #param {string} params.mentorName The name of the mentor.
*/
export const menteeMentorMatched = template`${'menteeName'} and ${'mentorName'} have been matched!`;
console.log(menteeMentorMatched({ menteeName: 'Jack', mentorName: 'Jill' }));
When typing that last line, I'd like autocomplete (in VSCode, if that's helpful) to list menteeName and mentorName plus their associated documentation, but since template returns a function that takes in ...values that's what it shows instead. How can I structure my JSDoc to show what I want instead of ...values?
Screenshot
Another option, of course, is to simply wrap the string builder functions in a function wrapper for documentation:
export const menteeMentorMatched = ({ menteeName, mentorName }) => `...`
I suspect this is the only way to make JSDoc work in this case. While this wouldn't be too bad, it's just a little more verbose than I'd like it to be since I'd like to avoid as much bloat as humanly possible in this file.
Is it possible to annotate JavaScript function parameters as can be done with attributes in C#?
Example (C#):
public void DoSomething([RequiredAction(Action="some_action")] Client client) {
// ...
}
The aim is - given a function - use reflection to inspect for instance the "type" of the parameters and perform the necessary conversions before calling it. JavaScript is indeed dynamically typed, but one could for instance want to use annotations to define the "type" of instances, a specific function expects (for instance, param should be an integer, or an array).
EDIT: The type aspect is only one possible use of an annotation. One could for instance also specify one must first run a specific function on that attribute or aspects like the maximum allowed length of an array.
One can of course use this answer and annotate the parameters by using specific parameter prefixes. For instance the Hungarian notation where sParam would identify the parameter should be a string. But that's not really convenient nor that extensible since it requires to specify names. Is there a more generic way to achieve this?
I like to use JSDOC, these are not checked at runtime but can be checked in certain editors (komodo edit for example) and stand alone applications (I think Google closure compiler is one). An example.
/**
* #namespace {Object} myObject
*/
var myObject = {};
/**
* This returns true if the operand inputArg is a String.
* #memberof myObject
* #name something
* #function
* #param {*} inputArg
* #returns {boolean}
*/
myObject.something = function (inputArg) {
return type inputArg === 'string';
};
If you want to require the argument type, rather than comment on its type, then the closest you can do is to make a function to simulate it.
function assertType(val, type) {
if (typeof val != 'object') {
if (typeof val != type.name.replace(/^./, function(f){return f.toLowerCase();}))
throw new Error("`" + val + "` is not of the data type `" + type.name + "`.");
}
else if (!(val instanceof type)) {
throw new Error("`" + val + "` is not of the data type `" + type.name + "`.");
}
}
function double(num) {
assertType(num, Number);
return num * 2;
}
console.log(double("malformed"));
/*
Error: `malformed` is not of the data type `number`.
at assertType:14:9
at double:18:2
at eval:21:13
at eval
*/
However, there isn't way to do this in the function declaration itself.
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).
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(){}
...
I'm using YUIDoc for the first time on a new project. I'm creating objects (isolated from one another) that will control creation and tracking of their own shapes, which are later written to the screen.
I have my main object - 'MakeOneOfThese' in the pseudocode below - with a 'ThingMaker' method to return shape definitions.
When I generated the YUIDocs I had a long list of classes at left, and no relationship between them. So I applied #for in the recommended way, but still my leftmost class list did not represent the hierarchy in the classes list or the body of the class docs. The classes list for example read:
MakeOneOfThese
shapeOne
shapeTwo
My workaround has been to prefix the hierarchy in the #class names with their parent classes. YUIDoc then gives me:
MakeOneOfThese
MakeOneOfThese.shapeOne
MakeOneOfThese.shapeTwo
I am keen to know how more experienced YUIDoc users have approached this, and how my solution may be replaced with something more appropriate, or improved upon.
The pseudocode below is ready to be dropped into a file and run through YUIDoc.
Many thanks for any time spent on responses.
Anthony
/**
The main constructor
#class MakeOneOfThese
#constructor
#param [options]{Object}
#param [options.gridSize] {Integer} The pixel dimensions for the grid
#default 20
**/
function MakeOneOfThese(options) {
var that= {
//game properties
//game methods
/**
A shape chooser
#method ThingMaker
#constructor
#param thingType {String}
#return MakeOneOfThese.ThingMaker.piece {object} an object representing definition information for the
named shape type
**/
ThingMaker: function(thingType) {
var PieceDefinitions = {
/**
A shape definition
#class MakeOneOfThese.shapeOne
#return MakeOneOfThese.ThingMaker.piece {object}
**/
shapeOne: function() {
var piece = {
name: "ShapeOne",
signature: {
n: [[1],[1,1],[0,1]],
e: [[0,1,1],[1,1]],
s: [[1],[1,1],[0,1]],
w: [[0,1,1],[1,1]]
}
}
return piece;
}
/**
A shape definition
#class MakeOneOfThese.shapeTwo
#return MakeOneOfThese.ThingMaker.piece {object}
**/
shapeTwo: function() {
var piece = {
name: "ShapeTwo",
signature: {
n: [[1],[0,1],[0,1]],
e: [[0,1,1],[1,1]],
s: [[1],[0,1],[0,1]],
w: [[0,1,1],[0,1]]
}
}
return piece;
}
}
}
return that;
}
Alternatively, you could also try JsDuck, which uses the same tags (almost all) as YUIDoc, and generates documentation that's not only prettier, but also maintains the class hierarchy
Instead of #class MakeOneOfThese.shapeTwo, you can us
#namespace MakeOneOfThese
#class shapeTwo