google / closure-compiler

A JavaScript checker and optimizer.
https://developers.google.com/closure/compiler/
Apache License 2.0
7.34k stars 1.14k forks source link

JSDoc support for array destructuring #1970

Open toxxmeister opened 8 years ago

toxxmeister commented 8 years ago

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.

brad4d commented 7 years ago

Duplicate of #1781

lll000111 commented 7 years ago

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

shicks commented 7 years ago

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

aaronbeall commented 6 years ago

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
 */
narekhovhannisyan commented 4 years ago

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;
}
lll000111 commented 4 years ago

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

franktopel commented 4 years ago

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?

brad4d commented 4 years ago

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

ctjlewis commented 3 years ago

This would be great, but is not exactly critical.

robfig commented 3 years ago

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

ctjlewis commented 3 years ago

@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
robfig commented 3 years ago

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

heydarm commented 3 years ago

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];
}
ctjlewis commented 3 years ago

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

joneldiablo commented 3 years ago

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?

ctjlewis commented 3 years ago

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

joneldiablo commented 3 years ago

@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