microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.22k stars 12.52k forks source link

JSDoc for methods with multiple signatures #407

Open Evgenus opened 10 years ago

Evgenus commented 10 years ago

First I've thought about union types but they can solve only part of the problem. Original discussion started here. In case codeplex had been abandoned, I copy my question here.

Please take a look at this function. https://github.com/digitalbazaar/forge/blob/master/js/rsa.js#L802-L885 As you can see from comments there are a lot of ways to use it. If I need to make a type definitions for it (actually I do) with JSDoc Intellisense support, then I have to copy general function description like 10 times. This will make my definition 3 times longer than source function and also very hard to modify. In libraries like forge almost all functions have being made that way.

Can you give me some tip about making definitions in such circumstances?

danquirk commented 10 years ago

What alternative would you like to see? You want to have the general description inherited when it's written on any overload? Or only when it's written against the implementation signature? Are argument descriptions inherited?

Evgenus commented 10 years ago

You want to have the general description inherited when it's written on any overload?

Yes. It would be nice.

Or only when it's written against the implementation signature?

I'm troubled about that only when writing definitions. So maybe for implementation it could be left as it is. My opinion is Yes.

Are argument descriptions inherited?

Yes, since there are already union types in jsdoc. Also jsdoc have names for arguments, not only index. I think in the most situations meaning of argument depends on its name and not on type. So if it is possible to show description for argument accordingly to its name, then argument description can be inherited.

Evgenus commented 10 years ago

Fresh thought. Maybe support of JSDoc tags @augments and @borrows will helps.

See https://code.google.com/p/jsdoc-toolkit/wiki/TagReference

basarat commented 10 years ago

Some people have used @see in the past e.g. :

https://github.com/borisyankov/DefinitelyTyped/blob/1ba83dded25175aa1239dd6ad0d6e0c81a3571ab/underscore/underscore.d.ts#L725-L738

Evgenus commented 10 years ago

@basarat @see doesn't help in VS right now. It shows only text of reference. And I can't imagine how to reference function/method with same name.

basarat commented 10 years ago

@see doesn't help in VS right now.

Agreed. Was just mentioning what people had tried. I'd like a @augments and @borrows jsdoc lookup in the language service as well.

sebastian-lenz commented 10 years ago

I've stumbled upon this problem too, copying the same comment to several signatures is not dry and leads to a lot of work and potential left overs when someone forgets to update all comments after some changes.

Some kind of inheritance makes sense to me. I would think of the implementation of a function to be the "root" containing the original comment. All signatures borrow this comment automatically unless it is (partly) overwritten in their own comments.

blakeembrey commented 8 years ago

Surprisingly, I just run into this now. It's certainly surprising, I always assumed it worked automatically without having to duplicate comments. See https://github.com/types/npm-pug/pull/5#discussion_r78636341.

I would expect this to be a cascade. The first JSDoc description and params should be copied to the following overloads automatically, except you could override the description/param later on by writing it out explicitly. Is this is a reasonable change? If so, I'd be very interested in a trying to patch it if there's a pointer on where to get started. If only so I can avoid merging hard to maintain workarounds like duplicating documentation.

mickdekkers commented 7 years ago

Related issue: https://github.com/Microsoft/TypeScript/issues/3294

It would be great if this worked as @blakeembrey described 👌

goodmind commented 6 years ago

Any progress?

MichaelTontchev commented 6 years ago

Definitely would love to see this implemented somehow. A function with 3 overloaded type definitions needs copy/paste for all three... not fun...

goodmind commented 6 years ago

@MichaelTontchev you mean that you can define overload now and it will work but with more copy-paste? can you show?

joma74 commented 6 years ago

Case here is a es5 class with a method member that is a candidate for method overloading. Using union types this member can be described as

/**
 * @todo See https://github.com/Microsoft/TypeScript/issues/407
 * 
 * @param {string} name 
 * @param {Function | BUFFERTYPE } [content]
 * @param {string} [type]
 * @return {Function | void}
 */
Runtime.prototype.createAttachment = function(name, content, type) {
...

In case of a Function for content, it should return a Function. In case of a BUFFERTYPE for content, it should return void.

Suggestion 1 Make overloaded definition in external d.ts(not on runtime.d.ts). P.S. Tried to apply it on the js es5 class level or member method level, but found not way to accomplish that.

Suggestion 2 Make overload list definition in three comment blocks.

/**
 * @param {string} name 
 * @param {Function} [content]
 * @param {string} [type]
 * @return {Function}
 */
/**
 * @param {string} name 
 * @param {BUFFERTYPE} [content]
 * @param {string} [type]
 * @return {void}
 */
Runtime.prototype.createAttachment = function(name, content, type) {
...

P.S. Tried to apply it on the member method level, but only the first was considered by TS as method signature.

Igmat commented 6 years ago

@mhegazy what kind of feedback is missing?

IMO, everything is pretty straightforward:

export function A(a: string): string;
export function A(a: number): string;
/**
 * Overloaded comment only for this signature
 * @description overloaded description only for this signature
 */
export function A(a: string, b: string): string;
/**
 * @param b overloaded description for `b` argument only for this signature
 * @returns overloaded description for `return` only for this signature
 */
export function A(a: string, b: number): string;
export function A(a: number, b: string): string;
export function A(a: number, b: number): string;
/**
 * Default comment for function A
 * @description default description for A
 * @param a default description for `a` argument
 * @param [b] default description for `b` argument 
 * @returns default comment for `return`
 */
export function A(a: string | number, b?: string | number): string {
    // implementation
}

So, each time when JSDoc is shown, it should merge default docs (that came from implementation in case of .ts file, or from most generic overload in case of .d.ts file) with docs that belong to particular overload, where last one takes precedence.

captain-yossarian commented 5 years ago

Does anybody working on it?

greaterking commented 5 years ago

I'm curious as well.

webOS101 commented 5 years ago

Ran into this one, too. Cascading down the first description would be the best, I think. Would greatly reduce the size of .d.ts files that document overloaded functions (In my case, curried functions).

seahindeniz commented 4 years ago

Check this gist. It may give you an idea https://gist.github.com/seahindeniz/4de7ad774c1043ddb47b4080667bb06a

sandersn commented 4 years ago

Complicating things even further, the new @deprecated tag would be ambiguous if appearing on the first signature; overloads are sometimes deprecated individually, but it's also common to deprecate an entire function.

As of the 4.0 beta, quick info says a function is deprecated if -- and only if -- the first signature has @deprecated. But the checker says a function is deprecated if any signature is deprecated. This is likely to change, but it illustrates how hard it is to find the correct behaviour.

chharvey commented 3 years ago

For what it’s worth, JSDoc has an @inheritdoc tag; maybe it can be repurposed for TypeScript documentation?

I like the idea of cascading. If an overload has @inheritdoc, it would inherit everything from the overload above, but would also include any additional documentation. This would help in the case of the new @deprecated tag.

Example 1: basic @inheritdoc usage

interface Vector {
   /**
    * Add a vector to this vector.
    * @param v - a vector to be added
    * @return - the vector sum
    */
   plus(v: Vector): Vector;
   /**
    * @inheritdoc
    * @param v - a tuple of 3 numbers to be added
    */
   // should inherit the description and @return tag, but not the @param tag
   plus(v: [number, number, number]): Vector;
}

Example 2: marking only last overload(s) as @deprecated

/** A function that does something. */
function foo(x: unknown): void;
/** @inheritdoc */
function foo(x: unknown, y: unknown): void;
/** @inheritdoc @deprecated */
function foo(x: unknown, y: unknown, z: unknown): void;
function foo(...args: any[]): any { /* implementation */ }

(This requires moving any deprecated overloads to the bottom, as we don’t want non-deprecated overloads to inherit the tag.)

/** A function that does something. */
function foo(x: unknown): void;
-/** @inheritdoc */
-function foo(x: unknown, y: unknown): void;
/** @inheritdoc */
function foo(x: unknown, y: unknown, z: unknown): void;
+/** @inheritdoc @deprecated */
+function foo(x: unknown, y: unknown): void;
# need to move 2-arg overload to bottom, otherwise the 3-arg overload would inherit the `@deprecated` tag
function foo(...args: any[]): any { /* implementation */ }
KutnerUri commented 3 years ago

I found you can just put the main declaration first, and the overloads after, like so:

/** deserialize a componnet id from raw object */
static fromObject(object: ComponentIdObj, scope?: string) {
    // ...
}
/** provide scope separaetly, e.g. fromObject({ name: 'button' }, 'teambit.base-ui') */
static fromObject(object: Omit<ComponentIdObj, 'scope'>, scope: string): ComponentID;
static fromObject(object: ComponentIdObj, scope?: string): ComponentID;

VSC will know to show the correct overload description:

Screen Shot 2021-07-13 at 12 00 17 Screen Shot 2021-07-13 at 12 00 32

VSC isn't showing the strikethrough for @deprecated overloads, though.

EDIT: I'm getting Function implementation name must be 'fromObject'.ts(2389) on the next method, so maybe it's not working correctly with typescript :(

unbyte commented 3 years ago

any progress?😀

P-Daddy commented 2 years ago

There is sort of a way to do this.

You can create an interface with a single any member having the same name as your overloaded function and apply docs to that. The downside is that @parameter docs don't seem to propagate correctly, at least not in VS Code.

interface Docs {
    /**
     * Documentation for `foo` function.
     * @param param1 Param 1 is important.
     * @param param2 So is param 2!
     * @returns A tuple of its arguments.
     * 
     * @example
     * ```ts
     * const result = foo("hello", "goodbye");
     * ```
     */
    foo: any;
}

interface Overloads extends Docs {
    foo<T>(param1: T, param2: T): T[];
    foo<T, U>(param1: T, param2: U): [T, U];
}

Hovering over either overload shows the correct documentation (but keep reading after the pictures for a caveat):

image

And it shows correctly in the suggestion list, too:

image

The only downside is that you don't get parameter information at the call site:

image

So you still have to duplicate the parameter documentation for each overload if you want that.

interface Overloads extends Docs {
    /**
     * @param param1 Param 1 is important.
     * @param param2 So is param 2!
     */
    foo<T>(param1: T, param2: T): T[];
    /**
     * @param param1 Param 1 is important.
     * @param param2 So is param 2!
     */
    foo<T, U>(param1: T, param2: U): [T, U];
}

image

Although notice that you lose the @returns and @example in this case.

Personally, if the parameter information would propagate correctly, and if adding documentation didn't lose some that was there before, I'd consider this issue resolved. But I'm not actually sure if it's a TypeScript issue or a VS Code issue.

geoffreytools commented 1 year ago

Something I noticed with higher order functions: since there is no way to have a JSDoc description show up in a tooltip when hovering over the return value of a function, using a workaround such as the one @P-Daddy suggested is going to totally obfuscate the little information the user had left: const foo: Overloads is not a tooltip I am happy to see, so I choose to bear with the duplication because a function signature is already something.

Now I would love it if there was a way to control how type aliases and interfaces show up in tooltips, but having some way to manage JSDoc duplication would also help.

SamB commented 1 week ago

https://github.com/microsoft/TypeScript/issues/51005 has a much clearer title: this title made me think it was a stale dupe of the issue for implementing @overload for use with JSDoc to declare types in JavaScript files.