How to extend a typedef parameter in JSDOC?
Asked Answered
A

3

74

Assuming you have the following code inside a ES6 class (documentation):

/**
 * @typedef Test~options
 * @type {object.<string>}
 * @property {array} elements - An array containing elements
 * @property {number} length - The array length
 */

/**
 * @param  {Test~options} opt - Option object
 */
test(opt){

}

Now I would like to document another function, let's name it test2. This function takes exactly the same options object, but needs another property parent.

How to document this without documenting redundant options? Redundant means:

/**
 * @typedef Test~options
 * @type {object.<string>}
 * @property {array} elements - An array containing elements
 * @property {number} length - The array length
 */

/**
 * @param  {Test~options} opt - Option object
 */
test(opt){

}


/**
 * @typedef Test~options2
 * @type {object.<string>}
 * @property {array} elements - An array containing elements
 * @property {number} length - The array length
 * @property {object} parent - The parent element
 */

/**
 * @param  {Test~options2} opt - Option object
 */
 test2(opt){

 }
Apoenzyme answered 20/4, 2016 at 8:24 Comment(1)
Reference on GitHub: github.com/jsdoc3/jsdoc/issues/1199Apoenzyme
O
76

I found this solution and this works very fine for me. originally from here

 /**
 * @typedef {Object} ChildType
 * @property {String} childProp
 *
 * @typedef {Base & ChildType} Child
 */

UPDATE: You can also use @extends to extend a jsdoc typedef.

/**
 * @typedef {object} ChildType
 * @extends Base
 * @property {string} childProp
 */

Second solution is better as Base & ChildType is not supported in plain jsdocs.

Obediah answered 26/3, 2019 at 13:56 Comment(8)
I can't really follow what the first solution is doing. This solution is very clear and conciseNipping
It's called an "intersection type"Stratiform
Cannot find name 'Base'.ts(2304)Tallyman
Intersection Types are Typescript, and are not supported in plain JSDoc. Everybody keeps conflating official supported JSDoc syntax with Typescript's implementation of JSDoc parsing. I suspect that this is because Typescript powers VSCode Intellisense, which has a pretty big market share, even among people who aren't actually writing Typescript.Jurisconsult
Or just inline like this : @typedef {Base & {childProp: string}} ChildDanas
@Jurisconsult : Very true, but with JSDoc not being very well maintained (at least not expanding language features in years), I think TypeScript's flavor of JSDoc is really the standard to look to now, not only for reasons of adoption but also it being the only candidate supporting automated discovery of third party modules via import(). FWIW, more rationales on why we're planning to switch the default to typescript mode at github.com/gajus/eslint-plugin-jsdoc/issues/834 even for plain JavaScript users (which tsc can process).Catlett
This & style syntax works in Visual Studio 2022 (probably older versions too), in a plain .js file. It probably works because Visual Studio uses Typescript to parse .js files.Splenitis
I'd like to know if that last piece works for intellisense too. I tested it in VSCode and it insists I can't use @extends without it being linked to @class - it shows me an error JSDoc '@extends' is not attached to a class.. In Webstorm I see no error, but it doesn't show me the base type properties either.Selwyn
U
17

Try it

/**
 * @typedef {object.<string>} Test~options
 * @property {array} elements - An array containing elements
 * @property {number} length - The array length
 */

/**
 * @param {Test~options} opt - Option object
 */
test(opt){

}

/**
 * @typedef {Test~options} Test~options2
 * @property {object} parent - The parent element
 */

/**
 * @param  {Test~options2} opt - Option object
 */
test2(opt){

}
Unicorn answered 23/11, 2016 at 14:11 Comment(6)
This is the best solution so far, because it specifies the type of Test~options2 as Test~options, linking to the latter. But this isn't ideal, because it doesn't repeat all the parameters of its "extended" type; it only lists the new params. JSDoc should work on better markup for this.Epineurium
Just want to leave a note here in case it is useful - I think you aren't forced into inheritance here in the way it seems - you can have something like: @typedef {Mixin1 & Mixin2} MyMix (but note when you do that it doesnt seem to like additional properties added to the mix in my editor)Drapery
I was trying this method to extend properties of HTMLElement but didn't work.Obediah
Andrew, see my comment on the other answer -- this answer works without using any extended syntax that's unsupported in "plain" JSDoc, where the intersection operator (&) has no meaning.Jurisconsult
Again, I'd like to know if this one works for intellisense too - VSCode doesn't show me any auto complete for the parent property, and if I write it as a property of an object typed as /** @type { Test~options2 } */ the intellisense tells me it is not recognized as a known property of that type (it seems to work ok with Jetbrains intellisense, though).Selwyn
This did not seem to work for me with VScode Intelli-sense. The second I make Test~options2 be of type {Test~options} it totally ignores all the @property declared on Test~options2.Baht
S
15

I don't like half-baked answers without a "full setup", so here is one:

/**
 * @typedef {Object} Person
 * @property {string} name - The person's name
 * @property {number} age - The person's age
 */
/**
 * @typedef {Object} WizardProperties
 * @property {string[]} magicPowers
 * @typedef {Person & WizardProperties} Wizard
 */
/** @type {Wizard} */
export const wizard = {
    name: "Harry",
    age: 20,
    magicPowers: ["brroooomm"]
}

It also works across multiple files, but you have to use the import('./person').Person style:

  • person.mjs:
/**
 * @typedef {Object} Person
 * @property {string} name - The person's name
 * @property {number} age - The person's age
 */
  • wizard.mjs:
/**
 * @typedef {Object} WizardProperties
 * @property {string[]} magicPowers
 * @typedef {import('./person').Person & WizardProperties} Wizard
 */
/** @type {Wizard} */
export const wizard = {
  name: "Harry",
  age: 20,
  magicPowers: ["brroooomm", "wheeeeeeeeeeeew"]
}
Scoutmaster answered 29/11, 2022 at 12:8 Comment(1)
Great answer. I'll add that when doing this across files, it can be nice to first @typedef {import('./something').Person} Person and then @typedef {Person & WizardProperties} Wizard — depending on the verbosity of the import path and types in question.Names

© 2022 - 2024 — McMap. All rights reserved.