63

Can I define all custom types in a separate file (e.g. types.jsdoc), so that they can be reused throughout the application? What's the right way to do it?

/**
 * 2d coordinates.
 * @typedef {Object} Coordinates
 * @property {Number} x - Coordinate x.
 * @property {Number} y - Coordinate y.
 */
2
  • Yes you can. You may have to add @global to the definitions, or experiment with the different ways to namespace stuff in JSDoc (confusing, IMHO, and for my own purposes which only is WebStorm inlineinfo/help and HTML API documentation works well).
    – Mörre
    Commented Oct 1, 2017 at 21:44
  • I had the same question using Visual Studio Code. I suggested this answer which you may find useful. Commented Apr 19, 2019 at 20:51

7 Answers 7

28

You can define types in a module (eg. typedefs.js). The module contains your JSDoc typedefs and can simply export an unused property.

// typedefs.js
/**
 * @typedef foo
 * @property {string} bar
 */

// etc.

exports.unused = {};
// or export {};

To use it, import the module where you need to reference these typedefs:

const typedefs = require("./typedefs");
/** @type {typedefs.foo} */
const fb = { bar: "hello" };

You may wish to annotate typedefs.js as a @module or @namespace. Because I'm using "tsd-jsdoc" to generate a types.d.ts file, and due to the way TypeScript now interprets modules vs. namespaces, I've annotated my typedefs.js file as a @namespace and documented each typedef as a member of that namespace:

/**
 * @namespace typedefs
 */

/**
 * @typedef foo
 * @property {string} bar
 * @memberof typedefs
 */

Hope that helps.

2
  • 4
    If you are using ES6 import/export, you can use export {}; to export nothing while still marking the file as a module.
    – Phil Kang
    Commented Aug 14, 2020 at 11:40
  • 1
    With \@module, the types would work in VSCode, but the links in generated docs were broken. With \@namespace, both VSCode and generated docs work. Been looking for this solution for a long time. Thanks!
    – W. Murphy
    Commented Aug 3, 2022 at 21:17
16

This is a TypeScript-flavored JSDoc specific answer, but I'm having success using a triple-slash directive to "import" all the types from another file. This has the advantage of not actually adding an unused import which can upset linters and bundlers.

I'm putting my shared types in one file called typedefs.js like this:

// typedefs.js
/**
 * @typedef {Object} Foo
 * @property {string} bar
 */

/**
 * @typedef {Object} Baz
 * @property {number} buzz
 */

and then using /// <reference path="typedefs.js" /> in the other files to access the shared types like this:

// randomThing.js
/// <reference path="typedefs.js" />

/**
 * Turn a Foo into a Baz
 *
 * @param {Foo} a
 * @return {Baz}
export function (a) {
  return { buzz: a.bar.length };
}

The tricky thing though is that now typedefs.js is just being referenced in a comment, bundlers like rollup miss it completely. So I'm combining it with my old consts.js that exports a few constants and is imported in at least one place. That way the typedefs are still included in the rollup output.

I hope someone else finds this helpful.

p.s. rollup will completely exclude a pure JSDoc typedefs.js file _even if you have import './typedefs.js' because of tree-shaking! Gotta run rollup with --no-treeshake to keep those comments in the rollup output.

4
  • well. maybe I spoke too soon. doesn't seem to be working now, sheesh. Commented Feb 2, 2020 at 21:22
  • 1
    Actually this was the one option that worked for me. VSCode 1.44.0
    – Fr0sT
    Commented May 25, 2020 at 12:46
  • does it render on webpage when we use the typedefs to one of the function param. @WilliamHilton Commented Sep 4, 2020 at 7:46
  • worked like a charm thank you <3 wonder if it works for external differences (urls) Commented Aug 7, 2021 at 20:13
8

In vscode, the import('./path/to/types.js').def tag works perfectly fine.

For e.g.
types.js

/**
 * @typedef {Object} connection
 * @property {String} id
 * @property {Number} pingRetries
 * @property {(data:Object) => void} sendJSON
 */
exports.unused = {};

And someFile.js

/**
 * @param {import('./types').connection} param
 */
const someFunc = (param) => {}

Also, note that the exports.unused = {} is necessary in the types.js file, otherwise the auto-import of import('./types') would not work and you may have to type it by yourself.

4

I just tried with VSCode and it works only if the separate file is opened in the editor. If not, external typedefs are typed as any

3
  • 2
    Thanks for the comment. I was excited to see that my types were picked up and I thought it was from the workspace, but it stopped when I closed the file as you'd suggested.
    – zavr
    Commented Jul 18, 2018 at 20:23
  • Yeah, this is what I just finally discovered... Did you ever find a solution for this? I've been trying things for hours now with no luck. :/ Commented Aug 15, 2020 at 22:21
  • Using v1.68.0 even though the file is not opened it is resolving the types. Yay!
    – BruceJo
    Commented Jun 9, 2022 at 22:57
2

I usually do something similar in my projects, the difference being I use the extension .js to name the file. Webstorm works perfectly and is able to check types and auto-complete just fine. It won't recognize the .jsdoc extension (I just checked), so stick to .js even if the file doesn't contain any code statement.

2

I've had success with simply creating my types in a typedefs.js file and referencing using the ts/vscode import(path/to/file).Foo tag. JSDoc does not support this syntax out of the box, so I suggest also using jsdoc-tsimport-plugin in order to parse your docs.

Eg: typedef.js:


/**
 * @typedef {Object} Foo
 * @property {string} id
 */

/**
 * @typedef {Object} Bar
 * @property {string[]} things
 */

// having to export an empty object here is annoying, 
// but required for vscode to pass on your types. 
export {};

coolFunction.js

/**
 * This function is super dope
 * @param {import('../typedef').Foo[]} foo - a foo
 * @return {import('../typedef').Bar[]} bar - an array of bars
 */

 export function (foo) {
    // do cool things
    return bar;
 }

I'm also using tsd-jsdoc to create a types.d.ts file, and this implementation is successfully creating the types. I had trouble with declaring modules and namespaces with the types file— just creating standalone typedefs for said models worked best for me.

1

To import types from another file, you can use these Typescript 5.5 directives:

/** @import {foo, bar} from './typedefs.js' */ // for several types
/** @import * as types from './typedefs.js' */ // for all types

/** @type {foo} */
const foo1 = 'foo';

/** @type {types.foo} */
const foo2 = 'foo';

An empty export must be specified in the typedefs.js file:

export {};
/** @typedef {string} foo */
/** @typedef {number} bar */

This works for me fine with VSCode 1.95.3 and ESLint 9.15.0 with eslint-plugin-jsdoc 50.6.0.
I do not know if this works in WebStorm or other IDE.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.