4

Given the following example:

/**
 * An outer function
 * @param {number} age - The age to pass to outerFunction
 * @returns {#What goes here?#}
 */
function outerFunction(age){
    return addTen(age)
}

/**
 * Adds 10 to the age
 * @param {number} age - The age to add 10 to
 * @returns {number} - The age + 10
 */
function addTen(age){
    return 10 + age
}

the outerFunction returns another function's result.

I thought of several ways to document this:

  • @returns {number} - We know that addTen returns a number, but what if this changes? We will have to update both (or each time it is returned, which could be a lot), which isn't maintainable.

  • @returns {function} - I am not sure if this is available in JsDoc. I couldn't find it anywhere. This also doesn't feel like it gives much information.

  • @returns {any} or - @returns {*} - This isn't particularly helpful to anyone reading the doc.

None of these feel correct to me for the reasons stated.

I guess I want something like

@returns {addTen.return}

So that I am essentially saying "outerFunction returns whatever type addTen does".

Note: these are in the same place in this example, but could be contained in multiple files, so using this approach doesn't work, unless it's possible to do this across multiple files.

How do we write JsDoc comments to document that the function returns another function?

Does something similar to my suggestion exist?

Community
  • 1
  • 1
Matt Lishman
  • 1,737
  • 1
  • 19
  • 33
  • *"the `outerFunction` returns another function"* No, it doesn't. It returns the *result* of *calling* another function, which is something else entirely. From the remainder of the question I think you know that, so it's not an answer, but... – T.J. Crowder May 10 '16 at 10:11
  • Ah, good point! My question still stands, in a slightly different format though... I will edit it. I would still like to know how to correctly document this. – Matt Lishman May 10 '16 at 10:12
  • 1
    *"We know that `addTen` returns a number, but what if this changes? We will have to update both (or multiple), which isn't maintainable."* Well, `outerFunction` is tightly bound to `addTen`, so any change to `addTen` will indeed affect `outerFunction`, which has knock-on effects -- to the documentation, but more importantly to its functionality. – T.J. Crowder May 10 '16 at 10:13
  • So, you're saying that when I change the way that `addTen` works, I also have to change each time anything returns it? I guess I want the documentation for `outerFunction` to reference `addTen`. – Matt Lishman May 10 '16 at 10:14
  • Only if you want `outerFunction`'s public contract to be tied to `addTen`, rather than just its implementation. – T.J. Crowder May 10 '16 at 10:15
  • What *should* `outerFunction` return? What's its contract? Imagine you replace the entire innards of `outerFunction` with something completely different, you *refactor* its implementation – presumably the return value should still be the same, because it's specified in its signature/API contract what it's supposed to return, no...?! – deceze May 10 '16 at 10:26
  • @deceze I think I understand. `outerFunction` should be the same return type because whatever calls `outerFunction` is expecting that return type. In this case, a number. So it's okay for `outerFunction` to have `@return {number}`. I think this answers the question. if you want to turn it into an answer I can accept it. – Matt Lishman May 10 '16 at 10:32
  • The unit test for that function answers the question. Have you written one? – Andy May 10 '16 at 11:21

1 Answers1

4

The caller of outerFunction will have certain expectations of what that function accepts as arguments and what it will return. The caller of outerFunction does not care what outerFunction does, only that its interface works as described. The caller of outerFunction does not know or care nor should they care that some function called addTen is involved in anything outerFunction does. In fact, someday you may rewrite the entire implementation of outerFunction to not call addTen anymore, but keep it behaving exactly the same way.

Treat every function individually as a black box. You're describing the interface of outerFunction, so describe what it does/is supposed to do. Don't describe it in terms of some other function which may or may not be related. If outerFunction is expected to return a number, describe it as such. If addTen also happens to return a number, well, what a coincidence.

I understand the impetus to want to implicitly tie the return value of one function to that of another, because that's how it's actually implemented, and you know... DRY and all... But in this case that's counter productive. It doesn't matter that you "repeat" the "same information" about the return type on two different functions; because you're not describing a connected thing. Both functions are independent black boxes with their own specific signature; that their implementation happens to be coupled is irrelevant for that, and may in fact change tomorrow. What is important is that the signature stays as described.

In fact, if addTen did change its return type (and implicitly so did outerFunction), that would be a big deal anyway that's not just going to blow over by implicitly updating some documentation. By changing the return type of any function, you're breaking a previously established contract, which will have a cascade of effects to every user of that function. Implicitly and automatically updating the return type of outerFunction is the least of your worries in such a scenario, as you might have to rewrite large swaths of code to conform to the new contract.

deceze
  • 471,072
  • 76
  • 664
  • 811