Search code examples
javascriptjsdoc

jsDoc @callback in VS Code


Is there some way to use the @callback tag of jsDoc in VS Code?

I tried the following code snipped from the jsDoc documentation of the callback tag:

/**
 * @class
 */
function Requester() {}

/**
 * Send a request.
 * @param {requestCallback} cb - The callback that handles the response.
 */
Requester.prototype.send = function(cb) {
    // code
};

/**
 * This callback is displayed as a global member.
 * @callback requestCallback
 * @param {number} responseCode
 * @param {string} responseMessage
 */

But VS Code does not seem to interpret the tag properly, thy type of the parameter remains any and the callback definition is not mention in the tooltip of the function.

Is there any workaround / plugin which enables this feature?


Solution

  • As of writing, @callback is not supported in VSCode, but there are workarounds (once VSCode supports callbacks, I will update this answer to reflect from which version onwards they work).

    See this issue to keep track of progress:

    [IntelliSense] [Salsa] doesn't recognize JSDoc callback parameter #7515

    EDIT 8 May 2018: A pull request with a fix was just submitted:

    Add callback tag, with type parameters

    EDIT 17 May 2018: Fix has been merged into TypeScript 2.9.1, will probably be in the next release of VS Code


    An incomplete workaround with @typedef is suggested in an early comment in the thread:

    /**
     * @typedef {function(number)} addedCallback
     */
    
    /**
     *@param {addedCallback} a
     */
    var add = function (a) { 
    
    }
    

    However, as noted a few comments later:

    The real use of @callback is to properly describe all the parameters of the callback and sometimes the expected return value and its effect on the current function.

    That comment also gives two options a more complete workaround: create a dummy function, or document the callback itself.

    If you use a build tool like Webpack, and set it up to eliminate unused variables and functions, the "dummy" functions won't show up your production code.

    Here are examples of both workarounds, based on code of my own:

    /**
     * A compare function to pass to `indices.sort()`, see also:
     * [`TypedArray.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/sort).
     *
     * @param {number} i
     * @param {number} j
     */
    function indexCompareFunction(i, j) {
      // dummy function for missing VSCode JSDoc callback support
      return i - j;
    }
    
    // Explicit
    /**
     * Creates a compare function to sort a _separate_ array
     * of indices, based on lexicographical ordering of
     * the array values. Produces a stable sort.
     * @param {*[]} array
     * @returns {indexCompareFunction}
     */
    function baseCompareFunc(array) {
      return (i, j) => {
        let vi = array[i],
          vj = array[j];
        return vi < vj ?
          -1 : vi > vj ?
            1 : i - j;
      };
    }
    
    // Inferred from callback JSDoc
    /**
     * @param {*[]} array
     */
    function baseCompareFunc2(array) {
      // Must store the function in a variable,
      // otherwise JSDoc does nothing!
      /**
       * @param {number} i
       * @param {number} j
       */
      let compareFunc = (i, j) => {
        let vi = array[i],
          vj = array[j];
        return vi < vj ?
          -1 : vi > vj ?
            1 : i - j;
      };
      return compareFunc;
    }
    

    EDIT: There is another shorter workaround, which relies on VSC's TypeScript support. It is technically not "standard" JSDoc. Decide for yourself if that is important or not. As explained by one of the devs:

    inside { .. } we allow TypeScript type syntax, and that is how you define a call signature in TS.

    /**
     * Creates a compare function for sorting a set of *indices*
     * based on lexicographical comparison of the array values
     * @param {*[]} array
     * @returns {{(i:number, j:number)=> number}}
     */
    function baseCompareFunc(array) {
      return (i, j) => {
        let vi = array[i],
          vj = array[j];
        return vi < vj ?
          -1 :
          vi > vj ?
            1 :
            i - j; // the part that makes this a stable sort
      };
    }