Closure annotation for variadic function - javascript

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; }

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.)

Function parameter type in JavaScript

I have a function to loop over an Array and look for a number.
function search(arr, num) { }
This function takes an Array and an Integer as the input. But I want the parameters to be of their respective types, is there any way I can set the type of the parameters.
if you want to enforce an specific data type you need to explicitly write that
/**
* looks for an specific item of the list.
*
* #param {Array<string>} data Array to search from.
* #param {number} num Number of where to find bla bla.
* #throws {TypeError} in case data or num do not have the expected type
* #returns {string} item found.
*/
export function search(data, num) {
if (!Array.isArray(data)) {
throw new TypeError("data should be an array");
}
if (typeof num !== "number") {
throw new TypeError("num should be a number");
}
return data[num];
}
JavaScript is a loosely typed language, there's no way to say declaratively that arr should be an array and num should be a number other than documentation (for instance, JSDoc). That would look like this:
/**
* Searches (insert fuller description here).
*
* #param {number[]} arr The array to search through.
* #param {number} num The number to (use? search for?)
* #returns (you can use the same {syntax} to say what the return type is)
*/
function search(arr, num) {
if (!Array.isArray(arr)) {
throw new Error(`'arr' argument must be an array`);
}
if (typeof num !== "number"/* Or use `Number.isInteger(num)` for an integer check [it includes the typecheck]*/) {
throw new Error(`'num' argument must be a number`);
}
// ...do the work...
}
You could check at runtime. That would look something like this:
function search(arr, num) {
if (!Array.isArray(arr)) {
throw new Error(`'arr' argument must be an array`);
}
if (typeof num !== "number"/* && perhaps a check for int if that part is really important*/) {
throw new Error(`'num' argument must be a number`);
}
// ...do the work...
}
There's a language built on top of JavaScript called TypeScript, which adds a static type system. It's then compiled to JavaScript for use with the browser, Node.js, etc. You may want to look into using that, if static typing is important for what you're doing.
In TypeScript, you'd do it like this (if you want the array to be an array of numbers):
function search(arr: number[], num: number) {
// ...
}
That doesn't require an integer, all JavaScript numbers are IEEE-754 floating point or BigInts.
javascript is a loosely type language. beacuse of it there are no types, you can actively check if the given parameter is array by .isArray() and .isInteger(), this both methods will return the booleen.
the answer given by pravat work but it can be devasting as they will not only set the type its will set its default value, in case if you didn't send the value of num then function will asume its 10 and start to work, which can be pain in debuging if you need strict type please look into typescript.

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[]}

Typing indexOf redefinition with Closure compiler

I am trying to redefine Array.prototype.indexOf for old IE versions. I am having trouble typing it correctly according to Google Closure Compiler.
It says that the type of #this is wrong.
if (!Array.prototype.indexOf) {
/**
* #this {Array}
* #param {*} item
* #param {number=} from: ignored
* #return {number}
*/
Array.prototype.indexOf = function(item, from) {
// ...
}
}
I get the following output
test.js:12: WARNING - variable Array.prototype.indexOf redefined with type \
function (this:Array, *, number=): number, original definition at \
externs.zip//es3.js:633 with type function (this:Object, *, number=): number
Array.prototype.indexOf = function(item, from) {
^
Surprisingly, changing #this {Array} by #this {Object} (though it does not make much sense) returns this even more obscure message:
test.js:12: WARNING - variable Array.prototype.indexOf redefined with type \
function (this:Object, *, number=): number, original definition at \
externs.zip//es3.js:633 with type function (this:Object, *, number=): number
Array.prototype.indexOf = function(item, from) {
^
Any hint on how to do it properly?
You can use #suppress {duplicate} to ignore this warning:
/**
* #this {Array}
* #param {*} item
* #param {number=} from: ignored
* #return {number}
* #suppress {duplicate}
*/
Array.prototype.indexOf = function(item, from) {
// ...
}
I am not sure about the implications redefining the method has on optimizations from the Closure Compiler in ADVANCED mode, though.
Array methods are generic, they should in fact take an Array-like value. The latest Closure Compiler defines it as:
/**
* Available in ECMAScript 5, Mozilla 1.6+.
* #param {T} obj
* #param {number=} opt_fromIndex
* #return {number}
* #this {{length: number}|Array.<T>|string}
* #nosideeffects
* #template T
* #see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf
*/
Array.prototype.indexOf = function(obj, opt_fromIndex) {};
Simply assigning the value works:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(item, from) {
// ...
}
}
Consider upgrading to a recent release of the Compiler.

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