How to specify resolution and rejection type of th

2019-03-08 07:37发布

问题:

I have some code that returns a promise object, e.g. using Q library for NodeJS.

var Q = require('q');

/**
 * @returns ???
 */
function task(err) {
    return err? Q.reject(new Error('Some error')) : Q.resolve('Some result');
}

How to document such a return value using JSDoc?

回答1:

Even if they don't exist in Javascript, I found that JSdoc understands "generic types".

So you can define your custom types and then use /* @return Promise<MyType> */. The following result in a nice TokenConsume(token) → {Promise.<Token>} with a link to your custom Token type in the doc.

/**
 * @typedef Token
 * @property {bool} valid True if the token is valid.
 * @property {string} id The user id bound to the token.
 */

/**
 * Consume a token
 * @param  {string} token [description]
 * @return {Promise<Token>} A promise to the token.
 */
TokenConsume = function (string) {
  // bla bla
}

It even works with /* @return Promise<MyType|Error> */ or /* @return Promise<MyType, Error> */.



回答2:

I tend to define an external type for the promise:

/**
* A promise object provided by the q promise library.
* @external Promise
* @see {@link https://github.com/kriskowal/q/wiki/API-Reference}
*/

Now you can describe in the @return statement of your function documentation what happens to the promise:

/**
* @return {external:Promise}  On success the promise will be resolved with 
* "some result".<br>
* On error the promise will be rejected with an {@link Error}.
*/
function task(err) {
    return err? Q.reject(new Error('Some error')) : Q.resolve('Some result');
}


回答3:

With JSDoc you can also create custom types using @typedef. I use this quite a bit so props/params that are strings or arrays link to a description of the type (like for string I created a typedef that includes the natives available to string (see the example JSDoc below). You can define a custom type in this same manner. This is because you can't use the object dot notation for returns like you can for @property to denote what is in the return. So in cases where you are returning something like an object, you can create a definition for that type ('@typedef MyObject) and then @returns {myObject} Definition of myObject.

I wouldn't go crazy with this, though, because types should be as literal as possible and you don't want to pollute your types, but there are cases where you want to define the type explicitly, and so you can document what is in it (a good example is Modernizr...it returns an object, but you have no documentation of it, so create a custom typedef that details what is in that return).

If you don't need to go that route, then as someone already mentioned, you can specify multiple types for any @param, @property, or @return by using the pipe |.

In your case, you should also document an @throws because you are throwing a new error: * @throws {error} Throws a true new error event when the property err is undefined or not available.

//saved in a file named typedefs.jsdoc, that is in your jsdoc crawl path
/**
    * @typedef string
    * @author me
    * @description A string literal takes form in a sequence of any valid characters. The `string` type is not the same as `string object`.
    * @property {number} length The length of the string
    * @property {number} indexOf The occurence (number of characters in from the start of the string) where a specifc character occurs
    * @property {number} lastIndexOf The last occurence (number of characters in from the end of the string) where a specifc character occurs
    * @property {string|number} charAt Gives the character that occurs in a specific part of the string
    * @property {array} split Allows a string to be split on characters, such as `myString.split(' ')` will split the string into an array on blank spaces
    * @property {string} toLowerCase Transfer a string to be all lower case
    * @property {string} toUpperCase Transfer a string to be all upper case
    * @property {string} substring Used to take a part of a string from a given range, such as `myString.substring(0,5)` will return the first 6 characters
    * @property {string} substr Simialr to `substring`, `substr` uses a starting point, and then the number of characters to continue the range. `mystring.substr(2,8)` will return the characters starting at character 2 and conitnuing on for 8 more characters
    * @example var myString = 'this is my string, there are many like it but this one is HOT!';
    * @example
    //This example uses the string object to create a string...this is almost never needed
    myString = new String('my string');
    myEasierString = 'my string';//exactly the same as what the line above is doing
*/


回答4:

Syntax currently supported by Jsdoc3:

/**
 * Retrieve the user's favorite color.
 *
 * @returns {Promise<string>} A promise that contains the user's favorite color
 * when fulfilled.
 */
User.prototype.getFavoriteColor = function() {
     // ...
};

Supported in the future?

/**
 * A promise for the user's favorite color.
 *
 * @promise FavoriteColorPromise
 * @fulfill {string} The user's favorite color.
 * @reject {TypeError} The user's favorite color is an invalid type.
 * @reject {MissingColorError} The user has not specified a favorite color.
 */

/**
 * Retrieve the user's favorite color.
 *
 * @returns {FavoriteColorPromise} A promise for the user's favorite color.
 */
User.prototype.getFavoriteColor = function() {
    // ...
};

See github discussion at: https://github.com/jsdoc3/jsdoc/issues/1197



回答5:

There's also another way of doing this even though it might be DEPRECATED. Emphasis on might since someone says it's deprecated (check the comments to that answer) while others say either one is fine. I'm reporting it anyway for the sake of completeness.

Now, take Promise.all() for example which returns a Promise fulfilled with an array. With the dot notation style it would look like shown below:

{Promise.<Array.<*>>}

It works on JetBrains products (e.g. PhpStorm, WebStorm) and it's also used in the jsforce docs.

At the time of writing when I try to auto-generate some docs with PHPStorm it defaults to this style even though I found poor reference to it.

Anyway if you take the following function as an example:

// NOTE: async functions always return a Promise
const test = async () => { 
    let array1 = [], array2 = [];

    return {array1, array2};
};

When I let PhpStorm generate the docs I get this:

/**
 * @returns {Promise.<{array1: Array, array2: Array}>}
 */
const test = async () => {
    let array1 = [], array2 = [];

    return {array1, array2};
};


回答6:

Here is what I like to do (which may be a little overdone):

/**
 * @external Promise
 * @see {@link http://api.jquery.com/Types/#Promise Promise}
 */

/**
 * This callback is called when the result is loaded.
 *
 * @callback SuccessCallback
 * @param {string} result - The result is loaded.
 */

/**
 * This callback is called when the result fails to load.
 *
 * @callback ErrorCallback
 * @param {Error} error - The error that occurred while loading the result.
 */

/**
 * Resolves with a {@link SuccessCallback}, fails with a {@link ErrorCallback}
 *
 * @typedef {external:Promise} LoadResultPromise
 */

/**
 * Loads the result
 *
 * @returns {LoadResultPromise} The promise that the result will load.
 */
function loadResult() {
    // do something
    return promise;
}

Basically, define the base promise with a link to some documentation (in this case I am linking to the jQuery one), define your callbacks that will be called when the promise either resolves or fails, then define your specific promise which links back to the callback documentation.

Finally, use your specific promise type as the return type.