Should I use a compatible class with #param in JSDoc? - javascript

My Python API returns a JSON object, for example:
{
a: 1,
b: 'hi'
}
I also have a class for it in the client:
class Sth {
constructor() {
this.a = null;
this.b = null;
}
}
I receive an object and pass it without modification to a function. Should I use #param like this, instead of a simple #param {*}?
/**
* #param {Sth} sth Is Sth appropriate here?
*/
function f(sth) {
// Do sth
}

Typically interaction with an outside/thirdparty resource is typed by an extern. If you're converting the code locally, that is: fetching and using the data to create an instance then your solution is correct: the class would be enough. If however you're fetching the data and the passing it to a constructor, you'll likely want to define a type in an externs.
When getting started with externs I find the closure debugger incredibly useful.

Yes, that is appropriate. JSDocs will treat the class as a type. If the Sth class isn't available in the same file as your function you'll probably need a JSDoc type definition in that file, e.g. with /** #typedef {import("./path/to/your/class/file.js").Sth} Sth*/
If you can avoid #param {*} you generally should, since having more type information is always better!

Related

JSDOC on socket.io emit

If I have socketio function like this :
_sock.emit('app_version',appversion,(response)=>{
console.log(response)
})
and I want to put a proper JSDOC , explaining that appversion is a string, and response is a string, how do i write it ?
currently tried some combination like #param , or #property in WebStorm, yet Webstorm still don't recognize that appversion and response type.
Kindly help
JSDoc works best if you type things as you declare them, which doesn't work very well if you're doing things inline (such as using an inline arrow function. Try something like this, declaring and typing your variables and functions before using them:
// Use #type to indicate the type of the next following variable
/**
* #type {string}
*/
let appversion;
// Use #param to indicate the param type of the next following function
/**
* #param {string} response
*/
function doThing(response) {
console.log(response);
}
_sock.emit('app_version', appversion, doThing);

Correct way to avoid 'Unresolved variable ...' errors when using variables returned as a JSON object from an API [duplicate]

I have a function that takes data from server:
function getData(data){
console.log(data.someVar);
}
WebStorm says that someVar is an unresolved variable.
How can I get rid of such warnings?
I see several options:
Suppress warnings in IDE settings;
Add a JSON source file with fields (details);
Use array-like syntax: data['some_unres_var'];
Also, WebStorm is offering me to create namespace for the "data" (add an annotation like /** #namespace data.some_unres_var*/), create such field, or rename it.
Use JSDoc:
/**
* #param {{some_unres_var:string}} data
*/
function getData(data){
console.log(data.some_unres_var);
}
JSDoc the object. Then its members.
/**
* #param data Information about the object.
* #param data.member Information about the object's members.
*/
function getData(data){
console.log(data.member);
}
#property for local variables (non parameters).
Tested in PyCharm. #Nicholi confirms it works in WebStorm.
The {{ member:type }} syntax Andreas suggested may conflict with Django templates.
Thanks to Jonny Buchanan's answer citing the #param wiki.
To document arrays of objects, use [] brackets as JSDoc suggests:
/**
* #param data
* #param data.array_member[].foo
*/
All other answers are incorrect for the general case. What if you don't get data as a parameter? You don't have JSDoc then:
function niceApiCall(parameters) {
const result = await ... // HTTP call to the API here
for (const e of result.entries) {
.. // decorate each entry in the result
}
return result;
}
WebStorm will warn that "result.entries" is an unresolved variable (field).
The general solution is to add an #namespace declaration:
function niceApiCall(parameters) {
/** #namespace result.entries **/
const result = await ... // HTTP call to the API here
for (const e of result.entries) {
.. // decorate each entry in the result
}
return result;
}
Destructuring use, Luke.
function getData(data){
const {member} = data;
console.log(member);
}
using a dummy js file with anonymous function expression returning the json literal, as written at http://devnet.jetbrains.com/message/5366907, may be a solution. I can also suggest creating a fake variable that will hold this json value, and use this var as a value of #param annotation to let WebStorm know what the actual type is. Like:
var jsontext = {"some_unres_var":"val"};
/** #param {jsontext} data */
function getData(data){
console.log(data.some_unres_var);
}
See also http://devnet.jetbrains.com/message/5504337#5504337
To remove the warnings on The WebStorm IDE you can simply uncheck the inspection options for:
Unresolved Javascript function
Unresolved Javascript variable
ps. this will remove the warnings on the IDE, that I don't think is the best idea, because we will lost one of the best utilities in a IDE like Webstorm, which can worsen the quality of our code.
Even so, if you want to follow in the menu: File > Settings > Editor > Inspections we can disable the Javascript warnings
Like the following picture:

Bad type annotation. Unknown type

I keep getting the warning mentioned above but can't figure out why. I've added deps.js and deps.js contains a reference to the type.
Here is the offending code:
goog.provide("MyCompany.MyApp.dom");
MyCompany.MyApp.dom.getArticleAmountInput_=function(){
return document.getElementById("articleAmount");
};
/**
* Gets the article amount input value
* #type {function(this:MyCompany.MyApp.dom):number} */
MyCompany.MyApp.dom.getArticleAmount=function(){
var tmp=this.getArticleAmountInput_();
return (tmp)?tmp.value():undefined;
};
In deps.js:
goog.addDependency('../../MyCompany/MyApp/dom.js', [ 'MyCompany.MyApp.dom'], []);
Code loads in html so it does find the class at runtime. Here is how I compile the code:
java -jar ../../compiler.jar \
--compilation_level=ADVANCED_OPTIMIZATIONS \
--formatting=PRETTY_PRINT \
--warning_level=VERBOSE \
--summary_detail_level=3 \
--js=MyCompany/MyApp/dom.js \
--js=closure/goog/deps.js \
--js_output_file=out.js
And it keeps giving me the warning:
WARNING - Bad type annotation. Unknown type MyCompany.MyApp.dom
[update]
Tried to leave out the goog.provide completely and leave out the js=closure/goog/deps.js when compiling and changed everything to lower case but keep getting the same warning on this code:
//goog.provide("mycompany.myapp.dom");
var mycompany={};
mycompany.myapp={};
mycompany.myapp.dom={};
mycompany.myapp.dom.getArticleAmountInput_=function(){
return document.getElementById("articleAmount");
};
/**
* Gets the article amount input value
* #type {function(this:mycompany.myapp.dom):number} */
mycompany.myapp.dom.getArticleAmount=function(){
var tmp=this.getArticleAmountInput_();
return (tmp)?tmp.value():undefined;
};
[update]
The thing is that when I add a typedef I get warnings that myapp.dom is never defined but when I let the code determine what the type is I get Bad type annotation.
Tried to add a typedef like so:
/**
* #typedef {{getArticleAmount:function(this:myapp.dom):?number}}
*/
myapp.dom;
But don't realy see why as goog.provide should create the myapp.dom and the compiler should know that.
[update]
I have created the following myapp.workflow from constructors. Now the compiler should recognize the type and Eclipse can code assist. Not sure if this is the way to do it but I'll try to re factor a small project.
Setting up the main types in types.js
// source: js/mmyapp/types.js
goog.provide("myapp.types");
/** #constructor */
var gooblediegoog=function(){};
/** #constructor */
gooblediegoog.prototype.WorkFlow=function(){};
/** #constructor */
gooblediegoog.prototype.Dom=function(){};
myapp.types=new gooblediegoog();
A file that isn't used at all in my code but tells Eclipse how to auto complete:
// source: js/myapp/forCodeAssist.js
/** #const {boolean} */
var ALLWAYSFALSE=false;
if(ALLWAYSFALSE){
/**needed for Eclipse autocomplete
* #constructor
*/
var MyApp=function(){};
MyApp.prototype.types=new gooblediegoog();
Window.prototype.myapp=new MyApp();
MyApp.prototype.workflow=new myapp.types.WorkFlow();
MyApp.prototype.dom=new myapp.types.Dom();
}
An implementation of workflow:
// source: js/myapp/workflow.js
goog.provide("myapp.workflow");
goog.require("myapp.types");
goog.require("myapp.dom");
/** #returns number|undefined */
myapp.types.WorkFlow.prototype.createOrder=function(){
return myapp.dom.getArticleAmout();
};
myapp.workflow=new myapp.types.WorkFlow();
window['console'].log(myapp.workflow.createOrder());
This can be converted to a myapp.workflow.createOrder=... syntax by replacing myapp.types.WorkFlow.prototype with myapp.workflow, removing myapp.workflow=new myapp.types.WorkFlow() and removing the goog.require("myapp.types"). Maybe this can be automated in the build/compile process if needed.
I am not sure if creating a single object with the help from a constructor function is much more expensive than just having goog.require create myapp.workflow and adding properties to it as I used to do (and as it's done in closure library).
Anonymous types (namespaces or singletons) don't have specific types in Closure-compiler and cannot be used as a type name in an annotation. Only constructors and interfaces can be used as new type names. (typedefs can be used but are really just shorthand annotations)
In many cases the compiler will correctly infer the types and you will not need to annotate it. In your specific case, it looks like the problem is your use of the this keyword. Using the this keyword in a static object / namespace is dangerous and not supported. The compiler will raise a warning. While you can suppress the warning with a #this annotation, that is not the correct action here as you are not specifically specifying the this reference by using .call or .apply.
The solution is to use the full object name:
/**
* Gets the article amount input value
*/
mycompany.myapp.dom.getArticleAmount=function(){
var tmp=mycompany.myapp.dom.getArticleAmountInput_();
return (tmp)?tmp.value():undefined;
};
For the full technical details, reference the This this is your this post.

How to document a type returned by a function (not constructor), and its methods

I'm using JsDoc3 and the Closure Compiler. I use a JsDoc template which outputs JSON for me to make HTML from it with a custom script.
I have a function which returns a custom class (but I don't have, nor need, a constructor), and I want to be able to document it (the function), and the functions present in the returned object.
myLib.defer = function() {
return {
'then': function() {},
'resolve': function() {},
'reject': function() {},
'notify': function() {},
'promise': function() {}
};
};
How do I document this? I don't have a constructor, nor I need one. I do not care that much about documenting the "class" (I don't need one, and besides, the object returned is documented in the #return of this function), the only thing I need is to document the functions that are present in the returned object, so they end up in the JsDoc's JSON output for me to grab.
Also, I'm using Closure Compiler's AVANCED_OPTIMIZATIONS mode, so if any workaround's side effects (such as useless code) will be supressed by that, it's a good workaround.
Unfortunately there is no way to do this that doesn't look awkward, but this works.
Firstly, one can document a class that doesn't have a constructor with an interface, as Chad mentioned. It looks like this:
/**
* Description of the Deferred interface.
* #interface Deferred
*/
function Deferred() {}
And one can document methods of it like this (for example):
/**
* Returns a Promise
* #function Deferred.promise
* #return {Promise} A Promise.
*/
Deferred.prototype.promise = function() {};
Then, on the function that returns your constructor-less class, you annotate your return like this:
myLib.defer = function() {
/** #type {Deferred} */
return {
'then': function() {},
'resolve': function() {},
'reject': function() {},
'notify': function() {},
'promise': function() {}
};
};
However, this will only pass Closure Compiler; it won't get rightly converted by the JsDoc3 JSON export template (maybe the regular one too), so we need this #class annotation (which CC ignores):
/**
* Description of the Deferred interface.
* #interface Deferred
* #class Deferred
*/
function Deferred() {}
With all of this in place, our docs will be generated correctly, and our code will compile to the same it did before - all the boilerplate is removed as dead code upon compilation.
Update: As Chad mentions, you wouldn't get a warning if you tried to instantiate a non-interface directly, which can be bad, and you may not like that (Interfaces will throw an error if instantiated).
One way to go about this is making the interface constructor #private to a #const object literal, as mentioned in his answer to that question.
Duplicate question. See How to document return in JavaScript.
In this case, since it looks like a library, you may want to declare and use an interface. See the jQuery externs for an example.

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