63

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){

 }
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
dude
  • 5,678
  • 11
  • 54
  • 81

3 Answers3

65

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.

Ajit Kumar
  • 1,157
  • 12
  • 21
  • I can't really follow what the first solution is doing. This solution is very clear and concise – Jared Beach Mar 30 '20 at 13:41
  • 3
    It's called an "[intersection type](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#intersection-types)" – shaedrich Aug 07 '20 at 16:14
  • Cannot find name 'Base'.ts(2304) – basickarl Nov 24 '20 at 12:07
  • 13
    Intersection Types are Typescript, and [are not supported](https://github.com/jsdoc/jsdoc/issues/1285) 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. – Coderer Dec 30 '20 at 11:04
  • Or just inline like this : ```@typedef {Base & {childProp: string}} Child``` – maljukan Jun 08 '21 at 00:59
  • 3
    @Coderer : 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 https://github.com/gajus/eslint-plugin-jsdoc/issues/834 even for plain JavaScript users (which tsc can process). – Brett Zamir Feb 08 '22 at 00:07
  • 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. – Gen1-1 Dec 31 '22 at 20:40
  • 1
    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. – Diego Bevilaqua Aug 02 '23 at 15:26
16

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){

}
dumistoklus
  • 160
  • 1
  • 6
  • 2
    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. – chharvey Jan 01 '18 at 06:22
  • 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) – Andrew Jun 14 '18 at 12:38
  • 1
    I was trying this method to extend properties of HTMLElement but didn't work. – Ajit Kumar Mar 26 '19 at 13:24
  • 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. – Coderer Dec 30 '20 at 11:06
  • 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). – Diego Bevilaqua Aug 02 '23 at 15:30
7

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"]
}
kungfooman
  • 4,473
  • 1
  • 44
  • 33