Skip to content

Discussion: Support @typedef Tag in JsDoc Comments [Salsa] #7758

Closed
@zhengbli

Description

@zhengbli

Currently type annotation is only allowed in TypeScript file. Although we do allow .d.ts files to be consumed by JavaScript files, there are already code bases employing JsDoc @typedef tag to define types within JavaScript files (Google Closure, etc). Supporting @typedef tag will enhance the editing experience for Salsa on these existing code bases.

The purpose of @typedef is to define a new type that can be used in other JsDoc comment annotations, similar to interface or type alias declaration in TypeScript. However, because there is no definite standard on the shape of @typedef, its has several popular dialects. The most common cases I found are as follows:

Case 1:

/**
 * @typedef {(string | number)} NumberLike
 */

Case 2:

/**
 * @typedef {Object} People
 * @property {number} id the unique id
 * @property {string} name - your name
 */

Case 3:

/**
 * @typedef People
 * @type {Object}
 * @property {number} id the unique id
 * @property {string} name - your name
 */

Case 4:

/** @typedef {(string | number)} */
var NumberLike;

Case 5:

/** @typedef {(string | number)} */
NumberLike;

Case 6:

/** @typedef {{ count: number }} */
People;

/**
 * @typedef {{
 *     id: number,
 *     name: string
 * }}
 */
People.person;

/**@type {People.person}*/
var p;

There are several difficulties / design choices for the current TypeScript type system to understand the above cases:

  1. Does the code need to be valid JavaScript code if ignoring the comments?
    For example:

      /**@typedef {string}*/
      StringType;

    is totally fine with the Google Closure compiler, while running the code directly will generate errors because the variable StringType is not defined.

  2. How does these types translate to TypeScript types?
    In simple cases, the @typedef functions similarly to type alias in TypeScript. However, the type name defined by @typedef can be a dotted name (as shown in case 6), while in TypeScript type alias name has to be an identifier. And like in case 6, a defined type (People) may be augmented later (added People.person property), which is hard to do for a type alias.

    The other route is to represent all the types as interfaces. This solves some of the the augmentation issue, because interfaces can have multiple declarations and be merged together, however it has flaws too. For example:

      /**@typedef {(string | number)} Id*/
      /**@typedef {string} Id.name*/

    in which the union type is hard to represent using interfaces.

  3. For case 4 or case 5, do we create both a value declaration and a type declaration for the variable? Imagine the following code:

    /**@typedef {(string | number)} */
    var foo;
    ...
    var foo = 10; // <- What is "foo" after this line? Should we give you an error / warning?
    if (foo) { // If I call "Go to declaration" on "foo", where will I go to?
        ...
    }

    In addition, if the variable only has a type declaration, potentially naming conflicts could happen. For exapmle, the following TypeScript code wouldn't have any error:

    type Test = string;
    function Test() { return 10; };
    var result = Test();

    However, we are reporting syntax errors for the corresponding JavaScript code using @typedef:

    /**@typedef {string}*/
    var Test;
    function Test() { return 10; };
    var result = Test();
  4. Types defined by @typedef should have a scope, and it may or may not be related to their parent node (they may not even have a parent node). For example:

    function A () {
        /**@typedef {string} TypeA*/
    
        /**@typedef {string} TypeB*/
        /**@param {string} p1*/
        function B () {
            ...
        }
    }

    Both TypeA and TypeB should live inside the block scope that function A contains.

Feedback is welcome to these questions, as I am not a heavy JsDoc user, so I might have missed some scenarios. I will keep prototyping potential solutions and update with a proposal once a reasonable design was reached.

Metadata

Metadata

Assignees

Labels

FixedA PR has been merged for this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions