tsdoc icon indicating copy to clipboard operation
tsdoc copied to clipboard

Handling/documenting merged declarations

Open natalieethell opened this issue 6 years ago • 10 comments

Pete and I were discussing ways that TSDoc can handle merged declarations, and wanted to summarize a couple ideas here.

Say you have a scenario where you want to export the same API with a different name. You could have something like

export { X as Y } from '...'

where Y is an enum. For certain reasons, we are considering splitting it up into something like the following instead:

export enum Y {
...
}

export type X = Y;
export const X = Y;

We likely still want both X's to be documented together. We have two tags in mind to solve this: @documentAs and @partOf.

@documentAs would be used to tell the documentation system how to present this API, for example grouping it under the "Enums" section even though it isn't technically an enum.

@partOf would be used to group the second definition with the first.

Using the example above, we might have something like the following:

/**
 * Another name for {@link Y}.
 * {@documentAs enum}
 */
export type X = Y;

/**
 * {@partOf (X:type)}
 */
export const X = Y;

I'm curious to see if anyone else has thought about this much and/or has any other ideas or suggestions.

natalieethell avatar Dec 12 '18 00:12 natalieethell

I'm curious in general about how documentation systems represent merged declarations, which seem to be a TypeScript-specific requirement.

Consider a definition like ApiReleaseTagMixin:

  • It is an interface that describes the mixin class.
  • It is a function that generates the mixin class for a given base class
  • It is a namespace that contains the static members of the mixin

Currently DocFX groups API items under headings by type (e.g. "Classes", "Interfaces", "Enums", and "Functions" in this example). Today ApiReleaseTagMixin will get three declarations that appear under "Interfaces", "Functions", and "Namespaces", and each of these entries would have its own documentation.

Using the TSDoc tags @natalieethell proposed above, we could maybe improve that:

/**
 * The mixin base class for declarations that have an associated release tag.
 *
 * @remarks
 * The interface describes the non-static members of the mixin class.
 * The namespace contains the static members.
 *
 * {@documentAs mixin}
 */
interface ApiReleaseTagMixin {
}

/** {@partOf (ApiReleaseTagMixin:interface)} */
namespace ApiReleaseTagMixin {
}

/**
 * Constructs a mixin base class conforming to the {@link (ApiReleaseTagMixin:interface)}
 * interface.
 * @param baseClass - The base class to extend
 * @returns a base class that implements `ApiReleaseTagMixin` and extends `baseClass`
 */
function ApiReleaseTagMixin<TBaseClass extends IApiItemConstructor>(baseClass: TBaseClass):
  TBaseClass & (new (...args: any[]) => ApiReleaseTagMixin) {
}

The @documentAs tag would cause this to go under a custom heading "Mixins" instead of "Interfaces". And @partOf tag causes the namespace signature to be documented with the interface (rather than getting its own documentation entry), like this:

ApiReleaseTagMixin Mixin

The mixin base class for declarations that have an associated release tag.

Signature:

interface ApiReleaseTagMixin
namespace ApiReleaseTagMixin

Remarks

The interface describes the non-static members of the mixin class. The namespace contains the static members.

Whereas the function would get its own documentation entry under the "Functions" section as usual:

ApiReleaseTagMixin Function

Constructs a mixin base class conforming to the ApiReleaseTagMixin interface.

Signature:

function ApiReleaseTagMixin<TBaseClass extends IApiItemConstructor>(baseClass: TBaseClass):
  TBaseClass & (new (...args: any[]) => ApiReleaseTagMixin);

Parameters

. . .

@dend

octogonz avatar Dec 12 '18 03:12 octogonz

We already established in https://github.com/Microsoft/tsdoc/issues/9 that function overloads should be documented separately. But Office Addin-ins encountered a bunch of function overloads that exist purely for technical reasons (due to two different enum representations in this case). For example:

/**
 *
 * Deletes the cells associated with the range.
 *
 * [Api set: ExcelApi 1.1]
 *
 * @param shift - Specifies which way to shift the cells. See Excel.DeleteShiftDirection for details.
 */
delete(shift: Excel.DeleteShiftDirection): void;
/**
 *
 * Deletes the cells associated with the range.
 *
 * [Api set: ExcelApi 1.1]
 *
 * @param shift - Specifies which way to shift the cells. See Excel.DeleteShiftDirection for details.
 */
delete(shift: "Up" | "Left"): void;

The @partOf tag could help with that problem, by allowing the above pattern to be simplified to this:

/**
 *
 * Deletes the cells associated with the range.
 *
 * [Api set: ExcelApi 1.1]
 *
 * @param shift - Specifies which way to shift the cells. See Excel.DeleteShiftDirection for details.
 */
delete(shift: Excel.DeleteShiftDirection): void;

/** {@partOf (delete:1)} */
delete(shift: "Up" | "Left"): void;

@AlexJerabek @Zlatkovsky

octogonz avatar Dec 12 '18 03:12 octogonz

How would this affect overloads with differently documented parameters? Here's a common example from our d.ts:

load(option?: Excel.Interfaces.StyleCollectionLoadOptions & Excel.Interfaces.CollectionLoadOptions): Excel.StyleCollection;

load(option?: string | string[]): Excel.StyleCollection;

load(option?: OfficeExtension.LoadOption): Excel.StyleCollection;

AlexJerabek avatar Dec 12 '18 18:12 AlexJerabek

The documentation would refer to the "main" declaration (i.e. the one that doesn't have an @partOf tag). If the other declarations had different parameters, they wouldn't get documented.

octogonz avatar Dec 12 '18 21:12 octogonz

Am I correct in thinking @partOf wouldn't help the Intellisense experience? Would the user see the partOf text or the "main" function's description?

AlexJerabek avatar Dec 12 '18 21:12 AlexJerabek

It depends. If @partOf was considered important enough to be made part of the TSDoc core standard, then to the extent that Visual Studio Code supports TSDoc, the IntelliSense would work.

octogonz avatar Dec 19 '18 02:12 octogonz

Just to be clear, individual parameters on overloads would not be explicitly documented with @param tags, correct? We'd have to fully contain all that data in the base function's description. That's all reasonable, though there might be cases with particular restrictions for the overloaded parameters (@ElizabethSamuel-MSFT might have such scenarios in Outlook).

Please let us know when this is functionality is supported in the web-build-tools suite.

AlexJerabek avatar Dec 21 '18 18:12 AlexJerabek

Update: PR https://github.com/microsoft/rushstack/pull/1646 adds most of the machinery needed to implement a @partOf tag.

octogonz avatar Nov 29 '19 15:11 octogonz

Would this work for merging interface docs into class docs? Using the example from https://github.com/microsoft/rushstack/issues/1921:

/**
 * @public
 */
export class MyWidget extends EventEmitter {
  // class implementation goes here
}

/**
 * {@partOf (MyWidget:class)}
 */
export interface MyWidget {
  // `on()` and `once()` declarations for MyWidget events
}

bajtos avatar Jun 09 '20 14:06 bajtos

Would this work for merging interface docs into class docs?

When designing @partOf, we did not think very much about how to handle API items that contain other nested items. Consider this example:

/**
 * {@partof (Example:namespace)}
 */
interface Example {
    a: number;

    /** Description 1 */
    b: number
}

namespace Example {
    /** Description 2 */
    export const b: string = 'b';

    export const c: string = 'c';
}

What does the table of contents show? Should the items be mixed together like:

  • Example (namespace)
    • a
    • b (from namespace)
    • b (from interface)
    • c

A different strategy would be to keep them as separate items, but with hyperlinks that makes it easy to switch between them:

  • Example (interface)
    • b
    • c
  • Example (namespace)
    • a
    • b

Something like this:

Example Interface

Related to: Example Class

The mixin base class for declarations that have an associated release tag.

Signature:

interface Example

octogonz avatar Jun 09 '20 17:06 octogonz