microsoft / tsdoc

A doc comment standard for TypeScript
https://tsdoc.org/
MIT License
4.71k stars 130 forks source link

Ways for pure JavaScript consumers to obtain type information from TypeScript libraries #120

Open octogonz opened 5 years ago

octogonz commented 5 years ago

@liamross brought up an interesting scenario in #115:

However, the fact that my documentation will have zero indication of the type of a parameter means that this could never serve as a replacement for JSDoc for me personally. If I write a library in TypeScript and it is consumed by a JavaScript application, I would like for there to be typings in the documentation, and not require that people are relying on IDE tooling or digging into the package to find types.

The first thing that drew me to this was the idea of it parsing types from the code to save the redundant declarations in the documentation.

If you build a library in TypeScript, the output includes declaration files (*.d.ts) that provide very accurate type information. (Arguably the best in the world! :-) ) However many JavaScript tools perform static type analysis based on .js files, and ignore the .d.ts files.

These tools generally expect to obtain their type information from JSDoc tags such as this sample that I pulled from a random blog post:

    /** @namespace */
    var util = {
        /**
         * Repeat <tt>str</tt> several times.
         * @param {string} str The string to repeat.
         * @param {number} [times=1] How many times to repeat the string.
         * @returns {string}
         */
        repeat: function(str, times) {
            if (times === undefined || times < 1) {
                times = 1;
            }
            return new Array(times+1).join(str);
        }
    };

The TypeScript equivalent would look like this:

/** @namespace */
export namespace util {
  /**
   * Repeat <tt>str</tt> several times.
   * @param {string} str - The string to repeat.
   * @param {number} [times=1] - How many times to repeat the string.
   * @returns {string}
   */
  export function repeat(str: string, times: number = 1): string {
    if (times === undefined || times < 1) {
      times = 1;
    }
    return new Array(times + 1).join(str);
  }
}

The JSDoc annotations such as @namespace, {string}, [times=1] are redundant because we already expressed all those things using type declarations. It would be annoying for developers to have to declare everything twice. And it would be error-prone because TypeScript developers generally don't rely on the JSDoc tags themselves, so they won't notice mistakes. (In my own anecdotal experience migrating code bases, I've found that these redundant tags are very frequently incorrect or inconsistent with the TypeScript types.)

The compiler does support JSDoc type annotations, so one possible idea would be for people to use JSDoc annotations instead of type declarations for the public parts of their API, since these annotations do end up in the emitted .js files. However this would be limiting, since JSDoc's type expressions are quite limited compared to all the things you can express in TypeScript. And it would be an awkward developer experience.

The "ideal" solution would be to improve the JavaScript tools to support .d.ts files when they consume a TypeScript library. But let's assume that's impractical for various reasons. What other approaches could we use, and can TSDoc help somehow?

octogonz commented 5 years ago

One possibility would be a compiler feature (or plugin?) that emits JSDoc type annotations in the output .js files, based on the compiler's type analysis. (I suspect this is closely related to another recent discussion, where people wanted the compiler to read plain .js files and generate .d.ts files based on its analysis.)

If the TypeScript source code has TSDoc comments, then in this approach the compiler would need to combine the TSDoc documentation with the type annotations to produce a JSDoc output.

It's tempting to expand the TSDoc language to optionally support every last JSDoc type annotation, and then the @microsoft/tsdoc library could be used to emit the JSDoc. However I'm sure there already exist plenty of specialized libraries for generating JSDoc, which do that very well, and are focused around that scenario. Keep in mind the goal is maximum compatibility with legacy JavaScript tools that are probably unable to adapt their parsers for this scenario. So I wonder to what extent the TSDoc input really needs to be consistent with the emitted JSDoc output.

What do you think? If someone has firsthand experience with this scenario, maybe they could provide some details.

aciccarello commented 5 years ago

Related: Microsoft/TypeScript#10

liamross commented 5 years ago

Hey thanks for extending this into an issue! Let me just elaborate a bit on what a dream scenario would be for me personally. Here's an example from the playground, but with typing included in the TypeScript. Note that as you mentioned above, ideally the typings would not be repeated in the actual documentation:

/**
 * Returns the average of two numbers.
 *
 * @remarks
 * This method is part of the {@link core-library#Statistics | Statistics subsystem}.
 *
 * @param x - The first input number
 * @param y - The second input number
 * @returns The arithmetic mean of `x` and `y`
 *
 * @beta
 */
function getAverage(x: number, y: number): number {
  return (x + y) / 2.0;
}

And here is an example of the documentation that said code would generate:


Summary

Returns the average of two numbers.

Parameters

Name Type Description
x number The first input number
y number The second input number

Return Value

Type Description
number The arithmetic mean of x and y

Remarks

This method is part of the [Statistics subsystem]().

Modifiers

@beta

liamross commented 5 years ago

What I'm not sure about is whether that would be unreasonably difficult to implement, or whether that is even a requirement / goal of this project.

octogonz commented 5 years ago

@liamross Hrmmm... I'm a little confused by your latest comment. What would the expected output look like for the .js files?

I'm reachable on Gitter if you want to chat directly.

octogonz commented 5 years ago

We chatted about this. @liamross clarified that for now he's mainly interested in generating web site documentation. I suggested to take at API Extractor or TypeDoc, both of which are complete documentation tools (whereas @microsoft/tsdoc is just the parser). They use the TypeScript compiler engine to crawl your library's API, and then generate a documentation web site based on your doc comments. API Extractor 6 has already integrated the TSDoc parser.