Skip to content

Instantly share code, notes, and snippets.

@ritch
Last active September 6, 2023 04:27
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ritch/9606670 to your computer and use it in GitHub Desktop.
Save ritch/9606670 to your computer and use it in GitHub Desktop.
What is the best way to document complex / dynamic objects as arguments to a function?
/**
* Below are several examples of methods that are difficult to clearly annotate with JSDoc annotations.
*/
/**
* @param {Object} people An index of people keyed by a person's name
* @returns {Object} map An index of zipcodes keyed by a person's name
*/
function find(people) {
return Object.keys(people).reduce({}, function(prev, cur) {
prev[cur] = zipFor(cur);
return prev;
});
}
/**
* @param {Object} options The options to set
*/
function configure(options) {
this.options = options;
}
/**
* @param {Object} options The options to set
*/
function configure(options) {
this.options = options;
}
/**
* @param {Object} options The options to set
* @param {Object} options.server The server options
* @param {String} options.server.host The server host
* @param {String|Number} options.server.port The server port
* @param {Array} options.remoteHosts An array of server options
*/
function betterConfigure(options) {
this.options = options;
}
/**
* The issue:
*
* - the documentation above is not clear and sometimes incomplete
* - an example would help, but would not be explicit
* - it is difficult to describe objects within arrays
*/
@raymondfeng
Copy link

@ritch
Copy link
Author

ritch commented Mar 17, 2014

@raymondfeng - what would that look like for a dynamic key? Eg. in a Map.

@rmg
Copy link

rmg commented Mar 17, 2014

I would make people an Array, which is easier to describe and then I'd describe the return value as a dictionary mapping names to zipcodes. I think the underlying problem is JavaScript's conflation of Object and a general purpose dictionary type.

@bajtos
Copy link

bajtos commented Mar 17, 2014

/**
 * @param {Object} people An index of people keyed by a person's name
 * @returns {Object} map An index of zipcodes keyed by a person's name
 */

I would use the syntax used by Google Closure Compiler (link)

{Object.<string, number>}
An object in which the keys are strings and the values are numbers.

/**
 * @param {Object.<string, Person>} people An index of people keyed by a person's name
 * @returns {Object.<string, ZipCode} map An index of zipcodes keyed by a person's name
 */

The description does not say the string key is a person name. A OOP purist would say you should wrap the primitive type in your custom type, but that seems like an overkill in a dynamic typed javascript.

/**
 * @param {Object.<Person.Name, Person>} people An index of people keyed by a person's name
 * @returns {Object.<Person.Name, ZipCode} map An index of zipcodes keyed by a person's name
 */

On the other hand, since we are not using the Closure compiler, I suppose it's ok to use types that don't exist, like Person.Name.

@ritch
Copy link
Author

ritch commented Mar 17, 2014

@bajtos - where would you define Person? Maybe that is what is missing. The ability to document a type that doesn't exist in the code...?

...then why even have a dynamic language... some might say.

@bajtos
Copy link

bajtos commented Mar 18, 2014

@ritch It was not clear to me that the structure is a nested key-value structure with not classes. Your original plain english comment did not provide any details on what "person" is. Perhaps we can assume the reader already knows that.


The Closure type system has a notion of records:

{{myNum: number, myObject}}
An anonymous type with both a property named myNum that has a value of type number and a property named myObject that has a value of any type.

Together with @typedef:

/**
 * @typedef {name:string, age:number} Person
 * @param {Object.<Person.Name, Person>} people
 * @returns {Object.<Person.Name, ZipCode} map
 */

To be honest, this approach makes sense only if there is a tool that can parse type definitions and display them in an easily consumable format. Otherwise it's probably better to describe the structure using plain english.

@reduardo7
Copy link

I am working with following:

/**
 * @typedef {{
 *    firstName: string,
 *    age: number
 *  }} Person
 *
 * @param {{
 *    [key: keyof Person]: Person
 *  }} p
 */

@Abrifq
Copy link

Abrifq commented Jul 3, 2020

I am working with following:

/**
 * @typedef {{
 *    firstName: string,
 *    age: number
 *  }} Person
 *
 * @param {{
 *    [key: keyof Person]: Person
 *  }} p
 */

Oh, thank you! I was trying to document a hybrid object (which has both static and dynamic properties) for my project and after observing <key,value> example and your example, I finally managed to achieve something like this:

/**@typedef {{
* [jobID:number]: Job,
* jobCount: number
* }} JobDatabase
*/

@xgqfrms
Copy link

xgqfrms commented Sep 6, 2023

Why is it not working for me?

An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.ts(1337)

/**
 * @typedef {{
*    firstName: string,
*    age: number
*  }} PersonX
*
*/

/**
* @param {{
*    [key: keyof PersonX]: PersonX
*  }} px
*/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment