26

Here is my jsdoc declaration.

How should I adjust it, so that MyNewType.logFirst property actually references logFirst function, which I've annotated below?

// my-new-type.js
/**
 * MyNewType definition
 * @typedef {Object} MyNewType
 * @property {function} logFirst
 * @property {function} logSecond
 */

/**
 * @param {number} first
 * @param {number} second
 * @returns MyNewType
 */
module.exports = (first, second) => {
  /**
   * logs first argument
   * @param {number} times
   */
  function logFirst(times) {
    for (let i = 0; i < times; i++) {
      console.log(first);
    }
  }

  /**
   * logs second argument
   * @param {number} times
   */
  function logSecond(times) {
    for (let i = 0; i < times; i++) {
      console.log(second);
    }
  }

  return {
    logFirst,
    logSecond
  };
};

It's important, that we keep this kind of "factory" structure.

Currently - this is what I receive: enter image description here

I want my IDE to bind MyNewType.logFirst with logFirst definition.

ProllyGeek
  • 15,517
  • 9
  • 53
  • 72
Pavel Polyakov
  • 866
  • 1
  • 8
  • 13

3 Answers3

40

Unless I'm mistaken, I don't see how the upvoted solution could ever work.

There is nothing like @typedef {MyNewType} MyNewTypein JSDoc, but rather @typedef {Object} MyNewType (to define the base type) (see docs)

Also, annotations are all mixed up in the given solution. For example, @param is only for function parameters and when declaring a specialized function type (see docs)

Last but not least, no offence but the upvoted solution is also wrong because it mixes up the var MyNewType type declaration (a function with two parameters and returning an object with two properties) and the actual returned value declaration (the two properties of the said object). In addition to not including the required brackets around the returned type.

EDIT: oh, I forgot that logFirst is not a number but a Function, which is another mistake in the solution proposed by the OP colleague.

I really cannot fathom why people would upvote such a broken solution...

Being by no means expert in JSDoc, I however think the following solution should fix all that, for new SO visitors:

(NOTE: I don't see why we would ever need two different typedef for the logFirst and logSecond functions as they have the exact same signature, but JFF I specified both)

/**
 * Declare our "logFirst" type (which is a function)
 *
 * @callback TypeFnLogFirst
 * @param {number} times
 * @returns {void}
 */

/**
 * Declare our "logSecond" type (which is a function)
 *
 * @callback TypeFnLogSecond
 * @param {number} times
 * @returns {void}
 */

/**
 * Declare our returned type
 * 
 * @typedef {Object} TypeObjTwoFns
 * @property {TypeFnLogFirst} logFirst
 * @property {TypeFnLogSecond} logSecond
 */

/**
 * Declare our "MyNewType" type (which is a function)
 *     with two parameters
 *     and a return value (object literal of type TypeObjTwoFns)
 *     (==> where the magic happens)
 * Note that "[at]typedef Function" is the same as "[at]callback" (as per the docs)
 *
 * @typedef {Function} TypeFnNewType
 * @param {*} first
 * @param {*} second
 * @returns {TypeObjTwoFns}
 */

/** @type {TypeFnNewType} MyNewType */
var MyNewType = (first, second) => {
    /** @type {TypeFnLogFirst} logFirst */
    function logFirst(times) {
        for (let i = 0; i < times; i++) {
            console.log(first);
        }
    }

    /** @type {TypeFnLogSecond} logSecond */
    function logSecond(times) {
        for (let i = 0; i < times; i++) {
            console.log(second);
        }
    }

    return {
        logFirst,
        logSecond
    };
};

Source: JSDoc typedef,callback,param and JSDoc member,var,type

Lideln Kyoku
  • 952
  • 9
  • 20
  • 1
    Note that "[at]typedef Function" is the same as "[at]callback" (as per the docs) - does the doc explicitly mention that somewhere? vscode does not seem to treat them the same in terms of intellisense, so just wanted to confirm – gaurav5430 Jun 01 '21 at 19:53
5

Just in case it helps anyone, cause OP is my colleague:

    // my-new-type.js
/**
 * MyNewType definition
 * @typedef {MyNewType} MyNewType
 * @param {number} first
 * @param {number} second
 * @property {function} logFirst
 * @property {function} logSecond
 * @returns MyNewType
 */

var MyNewType = module.exports = (first, second) => {
    /**
     * logs first argument
     * @typedef {number} logFirst
     * @param {number} times
     */
    function logFirst(times) {
        for (let i = 0; i < times; i++) {
            console.log(first);
        }
    }

    /**
     * logs second argument
     * @param {number} times
     */
    function logSecond(times) {
        for (let i = 0; i < times; i++) {
            console.log(second);
        }
    }

    return {
        logFirst,
        logSecond
    };
};

This has worked well for us.

ProllyGeek
  • 15,517
  • 9
  • 53
  • 72
  • 2
    Shouldn't your typedef be `@typedef {function} MyNewType`? MyNewType isn't defined before the typedef declaration so it can't be used as a type. Afterwards, in a different typedef, it could be though. – yuyu5 Sep 20 '21 at 16:38
3

Only if using Typescript as the type checker, there is a simpler solution IMO:

/** @typedef { (first: number, second: number) => 
{logFirst: (time: number) => void, logSecond: (time : number) => 
void}} MyNewType */

/** @type { MyNewType } */
var MyNewType = module.exports = (first, second) => {
   ...
}
Chen Peleg
  • 953
  • 10
  • 16