array of strings or a string enums in JSDoc - javascript

I have the following function which takes
yellow.
green.
orange.
function colorize (givenArg){
return givenArg
}
where givenArg could be a string or an array of strings.
I want to define the enums for the givenArg argument, but how?
/**
* #param {['green', 'yellow', 'orange']} givenArg
*/
function colorize (givenArg){
return givenArg
}
Currently, this only works for arrays, but how to make it work with both, an array of the strings, or a single string, without having to re-write yellow, green and orange two times?

In JsDoc, you can use the #typedef tag as follow:
/** #typedef {'green' | 'yellow' | 'orange'} X */
/** #typedef {X | X[]} ColorizeParam */
/**
* #param {ColorizeParam} givenArg
*/
function colorize(givenArg) { }
You may skip the intermediate step and directly type #param {X | X[]} givenArg if you want.
Based on #Konrad's answer.

Related

How can I specify the data that I extract for my IDE to know the type of object they are, if possible in Javascript? React native [duplicate]

Previously I've always documented my object parameters as follows:
/**
* Description of the function
*
* #param {Object} config - The configuration
* #param {String} config.foo
* #param {Boolean} [config.bar] - Optional value
* #return {String}
*/
function doSomething (config = {}) {
const { foo, bar } = config;
console.log(foo, bar);
// do something
}
But I am unsure what the best approach is with desctructured function parameter. Do I just ignore the object, define it somehow or what is the best way of documenting it?
/**
* Description of the function
*
* #param {String} foo
* #param {Boolean} [bar] - Optional value
* #return {String}
*/
function doSomething ({ foo, bar } = {}) {
console.log(foo, bar);
// do something
}
I feel like my approach above doesn't make it obvious that the function expects an object and not two different parameter.
Another way I could think of would be using #typedef, but that might end up being a huge mess (especially in a larger file with many methods)?
/**
* #typedef {Object} doSomethingConfiguration
* #property {String} foo
* #property {Boolean} [bar] - Optional value
*/
/**
* Description of the function
*
* #param {doSomethingConfiguration}
* #return {String}
*/
function doSomething ({ foo, bar } = {}) {
console.log(foo, bar);
// do something
}
This is how it's intended, as described in the documentation.
/**
* My cool function.
*
* #param {Object} obj - An object.
* #param {string} obj.prop1 - Property 1.
* #param {string} obj.prop2 - Property 2.
*/
const fn = function ({prop1, prop2}) {
// Do something with prop1 and prop2
}
So, your first example is pretty much correct.
Another example with some deeper nesting:
/**
* Nesting example.
*
* #param {object} param
* #param {number} param.a - First value
* #param {object} param.b - Wrapper
* #param {number} param.b.c - Second value
* #return {number} sum a and b
*/
const letters = ({a, b: {c}}) => a + c;
I personally use this one:
/**
* #param {{
a: number
b: number
}} param0
* #returns {number} The sum
*/
const func = ({ a, b }) => a + b;
Just create the object right there.
I also take advantage of TypeScript, and would declare obtional b as b? or b: number | undefined as JSDoc also allows unions
See JSDoc's "Documenting a parameter's properties":
/**
* Assign the project to an employee.
* #param {Object} employee - The employee who is responsible for the project.
* #param {string} employee.name - The name of the employee.
* #param {string} employee.department - The employee's department.
*/
Project.prototype.assign = function(employee) {
// ...
};
(Google Closure compiler type checking, that was based on but diverted from JSDoc, also allows #param {{x:number,y:number}} point A "point-shaped" object.)

How to override VSCode Intellisense inference with JSDoc comment

VSCode's Intellisense ignores my JSDoc comment and instead is inferring what the definition should be.
Function with JSDoc comment:
/**
* #param { Array<number> } arrayOfNumbers
* #return { number } sum - the sum of the arrayOfNumbers
*/
const getSumOfArrayElements = reduce (add, 0)
VSCode output:
const getSumOfArrayElements: (list: readonly any[]) => number
#param arrayOfNumbers
#return — sum - the sum of the arrayOfNumbers
Intellisense is defining getSumOfArrayElements as the definition for the return value of reduce (add, 0) (reduce with 2 arguments supplied)
Is there a way to have JSDoc comments have top priority in VSCode?
Thanks!
EDIT:
Just noting that this does work as expected
/**
* #param { Array<number> } arrayOfNumbers
* #return { number } sum - the sum of the arrayOfNumbers
*/
const getSumOfArrayElements = arrayOfNumbers => reduce (add, 0) (arrayOfNumbers)
Also, adding #function to the JSDoc comment doesn't change anything.

When should there be a dot in JSDoc generics?

I see some people write JSDoc for JavaScript generics like this (with a dot):
/** #param {Array.<Bar>} bars the bars that should be fooed*/
function foo(bars) {}
and others like this (without the dot):
/** #param {Array<Bar>} bars the bars that should be fooed*/
function foo(bars) {}
Whats the point of the dot? Which version is correct? When should I use one and when shouldn't I?
From a syntax point of view all these types expressions are valid when you run jsdoc index.js
/** #param {Array} x */
const a = x => x;
/** #param {Array.<string>} x */
const b = x => x;
/** #param {Array<string>} x */
const c = x => x;
/** #param {string[]} x */
const d = x => x;
It should be noted that the Google Closure Compiler doesn't seem to recognise the {<type>[]} type expression e.g. if you compile this:
/** #param {string[]} x */
const a = x => x;
You get the following warning:
JSC_TYPE_PARSE_ERROR: Bad type annotation. expected closing } See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information. at line 1 character 18
/** #param {string[]} x */
^
JSC_TYPE_PARSE_ERROR: Bad type annotation. expecting a variable name in a #param tag. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information. at line 1 character 18
/** #param {string[]} x */
^
See this Google Closure Compiler fiddle.
The static type checker in VS Code will complain about the last three function calls:
// #ts-check
/** #param {Array} x */
const a = x => x;
/** #param {Array.<string>} x */
const b = x => x;
/** #param {Array<string>} x */
const c = x => x;
/** #param {string[]} x */
const d = x => x;
a(['foo', 3]); // OK (we just need an array)
b(['foo', 3]); // ERR: 3 is not a string
c(['foo', 3]); // ERR: 3 is not a string
d(['foo', 3]); // ERR: 3 is not a string
So it pretty much seems like that {Array.<string>} and {Array<string>} are the same thing.
Personally I would favour {Array<string>} over {Array.<string>}. Why? The dot . is also a "namespace" separator:
/** #namespace */
const Burrito = {};
/** #constructor */
Burrito.beef = function () {};
/** #param {Burrito.beef} x */
const a = x => x;
a("foo");
a(new Burrito.beef());
The Google Closure compiler will issue a warning about the first call (the second call is fine):
JSC_TYPE_MISMATCH: actual parameter 1 of a does not match formal parameter
found : string
required: (Burrito.beef|null) at line 10 character 2
a("foo");
^
See this Google Closure Compiler fiddle.
If {Array<string>} and {Array.<string>} are genuinely the same thing (and I believe that to be true) then I would favour the former over the latter so as to keep the meaning of the . character unambiguous.
As per JSDoc documentation syntax with dot is correct.
https://jsdoc.app/tags-type.html
An array of MyClass instances.
{Array.<MyClass>}
// or:
{MyClass[]}

Closure annotation for variadic function

I'm writing code to be compiled by the Google Closure Compiler in advanced compilation mode. In some places in my code I have variadic functions which I'd normally write with an empty argument list. I'd access the passed arguments by inspecting the special variable arguments inside the body.
But how can I explain this to the Closure Compiler using annotations? If I don't do any parameter annotation, it complains that the function expects zero arguments. If I write an annotation #param {...*} (to mean an arbitrary number of arguments of arbitrary type), then it complains since the #param annotation must include the name of the parameter. And if I write #param {...*} arguments then it complains that the named argument does not occur in the list of arguments in the function implementation.
The only way I found to make Google happy is by including a dummy name in the argument list as well, even though that is never used. That in turn might confuse readers and perhaps also some linters. So I would prefer some alternative, if one exists.
/** WARNING - Function f1: called with 3 argument(s).
* Function requires at least 0 argument(s) and no more than 0 argument(s).
* #return {number} */
function f1() { return arguments.length; }
/** WARNING - Bad type annotation. expecting a variable name in a #param tag
* #param {...*}
* #return {number} */
function f2() { return arguments.length; }
/** WARNING - parameter arguments does not appear in f3's parameter list
* #param {...*} arguments
* #return {number} */
function f3() { return arguments.length; }
/** WARNING - Function f4: called with 3 argument(s).
* Function requires at least 0 argument(s) and no more than 0 argument(s).
* #return {number} */
function f4(/* ... */) { return arguments.length; }
/** This works but seems slightly confusing to me.
* #param {...*} var_args
* #return {number} */
function f5(var_args) { return arguments.length; }
window["run"] = function() {
return f1(1,2,3) + f2(1,2,3) + f3(1,2,3) + f4(1,2,3) + f5(1,2,3);
};
Compile this using
java -jar compiler.jar --compilation_level ADVANCED --warning_level VERBOSE foo.js
"f5" is the expected pattern.
/**
* #param {...*} var_args
* #return {number}
*/
function f5(var_args) { return arguments.length; }
An alternative is:
/** #type {function(...*):number} */
function f5() { return arguments.length; }

Google closure: trouble type checking parameters that should be functions

I'm messing around with the type checking in google's closure compiler. The type system seems useful, if perhaps not the most sophisticated out there. I'm happy with most of the limitations, but this one just seems a bit weird.
I'm seeing problems giving type annotations for functions passed as arguments. In particular, if the type of the passed function is itself not fixed. So for example, I'd like to write code similar to this:
/**
* #param {Array} xs
* #param {function(*) : boolean} f
* #return {Array}
*/
var filter = function (xs, f) {
var i, result = [];
for (i = 0; i < xs.length; i += 1) {
if (f(xs[i])) {
result.push(v);
}
}
return result;
};
filter([1,2,3], function (x) { return x > 1; });
Passing "--js_error checkTypes" to the compiler, I get this:
test.js:17: ERROR - left side of numeric comparison
found : *
required: number
filter([1,2,3], function (x) { return x > 1; });
^
So, what's wrong? Can I specify that a parameter ought to a function with one argument, without specifying the type of that argument? Am I doing something wrong, or is this just a limitation of the type checker?
Chad suggests annotating the anonymous function passed to filter to help the type-inference out a bit:
filter([1,2,3], function (x) { return /** #type {number} */ (x) > 1; });
That works ok for filter(), but it seems a little unsatisfying (why does the compiler need that annotation?), and doesn't work for more complex cases. For example:
/**
* #param {Array|string} as
* #param {Array|string} bs
* #param {function(*, *): *} f
* #return {Array}
*/
var crossF = function (as, bs, f) {};
/**
* #param {Array|string} as
* #param {Array|string} bs
* #return {Array}
*/
var cross = function (as, bs) {};
var unitlist = crossF(['AB', 'CD'], ['12', '34'], cross);
It seems like the type of everything here should be apparent to the compiler. And in fact it complains directly about matching the type of the function parameter:
test.js:52: ERROR - actual parameter 3 of crossF does not match formal parameter
found : function ((Array|null|string), (Array|null|string)): (Array|null)
required: function (*, *): *
var unitlist = crossF(['ABC', 'DEF', 'GHI'], ['123', '456', '789'], cross);
Accepted answer below addresses this case.
Change the declaration of the filter from "*" (everything) to "?" (unknown). The compiler only checks known types. So when the compiler tries to infer the function signature for the function expression at the call site, it resolves the parameter "x" to "?" (an unknown type) (which can be used as anything), instead of "*" (every possible type) which often needs to be restricted before use:
/**
* #param {Array} xs
* #param {function(?) : boolean} f
* #return {Array}
*/
var filter = function (xs, f) {
var i, result = [];
for (i = 0; i < xs.length; i += 1) {
if (f(xs[i])) {
result.push(v);
}
}
return result;
};
When there are no annotations on a function, the compiler assumes that it can take a variable number of arguments of any type and return any type. For this reason, many of the extern functions are annotated like this:
/** #return {undefined} */
function MyFunction() {}
This way they will properly type check.
For your case, the easiest solution is to type cast the argument to a number inside the function (note the extra parenthesis which are required):
filter([1,2,3], function (x) { return /** #type {number} */ (x) > 1; });
One common approach is to use the type annotation {!Function}, which accepts any function object.
The issue with the ALL type (*) has been reported here: Issue 708
This looks like a bug to me. You should file it here:
http://code.google.com/p/closure-compiler/issues/list
If you don't specify the types, it should be "unknown"(?) not "any"(*). The compiler doesn't (or shouldn't) type check the use of unknown types.

Categories

Resources