closure-compiler icon indicating copy to clipboard operation
closure-compiler copied to clipboard

JSDoc support for array destructuring

Open toxxmeister opened this issue 9 years ago • 17 comments

Currently there is no simple way to annotate destructured array parameters.

I propose a syntax similar to the current destructured objects syntax:

/**
 * @param {[x: number, y: number]} randomName
 * @return number
 */
function add([x, y]) {
    return x + y;
}

Due to the nature of destructured arrays, this issue is closely related to #379.

toxxmeister avatar Aug 22 '16 12:08 toxxmeister

Duplicate of #1781

brad4d avatar Oct 06 '16 20:10 brad4d

It's not really a duplicate, issue #1781 presumes an object.

I'm only commenting because I'm right now trying to find a syntax that works, and ideally one that WebStorm accepts too... I used a JSDoc syntax that ignored that destructuring even takes place which kept the IDE from complaining, but now that I create API docs in HTML I have to find something else - the parameters don't even show up in the output with this workaround...

lll000111 avatar May 05 '17 21:05 lll000111

I think what we need is tuple types, which is on our short list for once the NTI migration is complete.

shicks avatar May 05 '17 22:05 shicks

Additionally, destructuring rest assignment is impossible to represent:

function multiplySum([factor, ...numbersToSum]) {
    const sum = numbersToSum.reduce((a, b) => a + b, 0);
    return sum * factor;
}

And would require a tuple type that can represent "rest" like types:

/**
 * @param {[factor: number, ...numbersToSum: number[]]} args
 */

aaronbeall avatar Nov 07 '17 18:11 aaronbeall

Try like this, it's worked for me in VSCode.

/**
 * @param {[x, y]: [Number, Number]} randomName
 * @returns {Number}
 */
function add([x, y]) {
    return x + y;
}

narekhovhannisyan avatar Jan 17 '20 14:01 narekhovhannisyan

@narekhovhannisyan Just an aside, there are two different issues:

  1. JSDoc the format
  2. jsdoc3 the tool to create documentation from JSDoc comments

It's no use for some IDEs to interpret the format, at least I want the documentation producing capability of the tool right here in this repo.

lll000111 avatar Jan 17 '20 16:01 lll000111

There have been no updates on this despite being an open issue for almost 4 years. It has not even been assigned. Is there any hope this might get implemented any time this year?

franktopel avatar Feb 12 '20 10:02 franktopel

Sadly, I don't think adding this feature is on our 2020 roadmap.

brad4d avatar Feb 13 '20 21:02 brad4d

This would be great, but is not exactly critical.

ctjlewis avatar Oct 06 '20 20:10 ctjlewis

The need to annotate tuples comes up constantly when using React hooks (React.useState). This capability would be extremely useful.

ref: https://groups.google.com/g/closure-compiler-discuss/c/MsoqyuVZL1M/m/lOhWU6R8CAAJ

robfig avatar Oct 07 '20 00:10 robfig

@robfig Agreed. I applaud you for wanting to compile your React project as well, since they are notoriously bloated with dead code.

The truth is that the Closure Compiler is an internal Google tool, and that the people on this team do a great job of keeping it relatively up-to-date and feature-rich for open source use cases, but you're right that this is definitely a good request.

This is on my wish list along with class fields. Truthfully, React's useState pattern is not very intuitive nor very common, so it was always going to be a poor candidate for Closure Compiler accommodation. It can understand Array<type>, but not different types at different indices.

In the meantime, you can use:

const React = {
  /**
   * @param {number} num
   * @return {Array}
   */
  useState: (num) => [ num, (newNum) => console.log(newNum) ],
};

/** @type {Array} */
const stateTuple = React.useState(Math.random());

/** @type {number} */
const num = stateTuple[0];

/** @type {function(number):number} */
const updateNum = stateTuple[1];

And everything will be typed as expected. I know it's sad to not utilize array destructuring natively, but I would be shocked if this has a meaningful impact on the output size or performance. To test the typing and get output, let's log our variables at the end:

...
console.log({
  /** @type {number} */
  num,
  /** @type {function(number):number} */
  updateNum,
});

Produces:

var a=function(b){return[b,function(c){return console.log(c)}]}(Math.random());console.log({a:a[0],b:a[1]});

Behaves as expected:

VM58:1 {a: 0.6578095262052157, b: ƒ}

Test the typing by breaking a type:

...
// oops!
/** @type {function(string):number} */
  updateNum,
...

Compiler throws:

WARNING - [JSC_TYPE_MISMATCH] assignment to property updateNum of {
  num: number,
  updateNum: function(string): number
}
found   : function(number): number
required: function(string): number
  24|   updateNum,
        ^^^^^^^^^

0 error(s), 1 warning(s), 96.4% typed

These compilations were run with two options:

  1. Maximum compression and DCE with -O ADVANCED.
  2. Use typing with --use_types_for_optimization.

I recommend using the Compiler locally with npm i -g google-closure-compiler, and then the command is simply:

google-closure-compiler -O ADVANCED --use_types_for_optimization myfile.js

ctjlewis avatar Oct 07 '20 00:10 ctjlewis

Thanks for the guidance.

Since calling React.useState is a common thing to do, I was hoping to make it possible to do so with less ceremony. Going through a wrapper seems like it might be able to streamline things while maintaining type checks:

/**
* @template T
* @param {!T} value
* @return {{val: !T, set: function(!T)}}
*/
WrapReact.useState = function(value) {
  const state = React.useState(value)
  const val = /** !T */ (state[0]);
  const set = /** function(!T) */ (state[1]);
  return {val: val, set: set}
}

const num = WrapReact.useState(Math.random());

console.log(num.val);
num.set(Math.random());

It seems to work in the online playground

robfig avatar Oct 10 '20 00:10 robfig

These work in VS Code:

/**
 * @param {[x: number, y: number]} param
 * @returns {[x: number, y: number]}
 */
function doNothing([x, y]) {
    return [x, y];
}

and

/**
 * @param {[number, number]} param
 * @returns {[number, number]}
 */
function doNothing([x, y]) {
    return [x, y];
}

heydarm avatar Apr 26 '21 17:04 heydarm

Yes, but the compiler can't handle them as far as I'm aware.

ctjlewis avatar Apr 28 '21 03:04 ctjlewis

Additionally, destructuring rest assignment is impossible to represent:

function multiplySum([factor, ...numbersToSum]) {
    const sum = numbersToSum.reduce((a, b) => a + b, 0);
    return sum * factor;
}

And would require a tuple type that can represent "rest" like types:

/**
 * @param {[factor: number, ...numbersToSum: number[]]} args
 */

soooo, the destructuring rest assignment has any way to be documented?

joneldiablo avatar Jul 28 '21 15:07 joneldiablo

@joneldiablo, I would not count on it. I have pitched Google about allocating more resources to the compiler, but it was shot down.

I would not count on new features being added anytime soon, and this tool is maintained largely for the sake of internal Google projects.

ctjlewis avatar Jul 28 '21 15:07 ctjlewis

@joneldiablo, I would not count on it. I have pitched Google about allocating more resources to the compiler, but it was shot down.

I would not count on new features being added anytime soon, and this tool is maintained largely for the sake of internal Google projects.

oh, sad. =( thankyou

joneldiablo avatar Jul 28 '21 16:07 joneldiablo