microsoft / tsdoc

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

Question: tsdoc for function type declaration #184

Open xiaoxiangmoe opened 5 years ago

xiaoxiangmoe commented 5 years ago
export type OnEnter = (success: boolean) => void;
export interface OnLeave {
  (success: boolean): void
}

How should we reasonably add the type declaration of the above function type 'OnEnter' and 'OnLeave' ?

I tried as follows:

image

image

octogonz commented 5 years ago

Good question!

I would document it like this:

/**
 * A callback that occurs after the control receives focus.
 * @param success - true if successful
 */
export type OnEnter = (success: boolean) => void;

/**
 * A callback that occurs after the control receives focus.
 */
export interface OnLeave {
  /**
   * The call signature for `OnLeave`.
   * @param success - true if successful
   */
  (success: boolean): void
}

The TSDoc design focuses mainly on how comments get parsed. The spec will give better guidance about /where/ comments should go (but this is guidance for implementors; it doesn't affect the parser library at all).

You could consider API Extractor to be a functionally complete reference implementation of a TSDoc application. API Extractor distinguishes specific syntax elements as being "API items" (see ApiItem.ts and AstDeclaration.ts). These are the things that get their own entry on the documentation website. For OnEnter it would be the ts.SyntaxKind.TypeAliasDeclaration declaration. For OnLeave it would be the ts.SyntaxKind.CallSignature.

Some additional thoughts:

xiaoxiangmoe commented 5 years ago
/**
 * A callback that occurs after the control receives focus.
 */
export type OnEnter = 
  /**
   * 
   * @param success - true if successful
   */
  & ((success: boolean) => void)
  /**
   * 
   * @param message - success message
   */
  & ((message: string) => void)

/**
 * A callback that occurs after the control receives focus.
 */
export interface OnLeave {
  /**
   * The call signature for `OnLeave`.
   * @param success - true if successful
   */
  (success: boolean): void
  /**
   * The call signature for `OnLeave`.
   * @param message - success message
   */
  (message: string): void
}

I think this can better represent overloading.

xiaoxiangmoe commented 5 years ago

It could be ambiguous, though.

// here there are two different parameters called "success"
   export type OnEnter = ((success: boolean) => void) 
     | ((success: ISuccessCode, details: string) => void);

In typescript, we generally use the intersection type of the functions to represent overloading.

If tsdoc correctly parses the following notation, then it has the same ability to write doc as interface-with-a-call-signature.


/**
 * A callback that occurs after the control receives focus.
 */
export type OnEnter = 
  /**
   * 
   * @param success - true if successful
   */
  & ((success: boolean) => void)
  /**
   * 
   * @param success - success code
   * @param details - the details
   */
  & ((success: ISuccessCode, details: string) => void);
octogonz commented 5 years ago

This design has a problem: it introduces the possibility that /** */ comment can appear anywhere in an expression. For example, should this be valid?

/**
 * A callback that occurs after the control receives focus.
 */
export type OnEnter = 
  &
  /**
   * the first overload
   */
  (
    (
      /** 
       * @param success - true if successful 
       */ 
      success: boolean
    ) => /** The return value */ void
  )
  & (
   /**
    * The second overload
    * @param success - success code
    * @param details - the details
    */
    (
      success: ISuccessCode, 
      details: Set</** this value must not be empty */ string>
    ) => void
  );

If not, which parts of an expression are allowed to have doc comments? We would need to make rules about where a comment can go, and I suspect those rules would be fairly complicated. It might be difficult for a user to understand /why/ a comment is allowed in one place, but not in another place. A goal of TSDoc is that the notation should be easy to learn from looking at examples, without having to consult a manual. And its interpretation should be easy to predict, without needing to run it through a parser/renderer.

There's also a question of how a documentation engine is supposed to present these comments. Documentation templates tend to be based on stereotypes ("functions", "interfaces", "classes", etc.) Free-form type algebra expressions are challenging to document, at least if you want great documentation that looks professional.

Cheatoid commented 2 years ago

Bump. JSDoc has @callback for this purpose. Not particularly happy with TSDoc approach, but at least it somewhat works, albeit weird that it omits parameter documentation if you place caret on the name of the parameter.

theuntitled commented 1 year ago

Bump. Still looking for a good solution, too. I currently use a workaround like below, but i'm not really happy with the type unawareness inside the handler function:

type SuccessCode = 200 | 201;

/**
 * A callback that occurs after the control receives focus.
 * @param success true if successful
 */
declare function OnEnterFn(success: boolean): void;

/**
 * A callback that occurs after the control receives focus, contains details.
 * @param success success code
 * @param details the details
 */
declare function OnEnterFn(success: SuccessCode, details: string): void;

/**
 * Type description when hovering over OnEnter.
 */
export type OnEnter = typeof OnEnterFn;

const handler: OnEnter = (success: boolean | SuccessCode, details?: string | undefined) => {
    if (typeof success === "boolean") {
        console.log("overload 1", success);
    } else {
        console.log("overload 2", success, details);
    }
};

const foo = (onEnter: OnEnter) => {
    onEnter(true);
};

const bar = (onEnter: OnEnter) => {
    onEnter(201, "more details");
};

foo(handler);
bar(handler);