Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling/documenting merged declarations #137

Open
natalieethell opened this issue Dec 12, 2018 · 10 comments
Open

Handling/documenting merged declarations #137

natalieethell opened this issue Dec 12, 2018 · 10 comments
Labels
general discussion Not a bug or enhancement, just a discussion octogonz-parser-backlog Items for @octogonz to consider during the next iteration on the parser

Comments

@natalieethell
Copy link

natalieethell commented Dec 12, 2018

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.

@octogonz octogonz added the general discussion Not a bug or enhancement, just a discussion label Dec 12, 2018
@octogonz
Copy link
Collaborator

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
Copy link
Collaborator

We already established in #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

@AlexJerabek
Copy link

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;

@octogonz
Copy link
Collaborator

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.

@AlexJerabek
Copy link

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?

@octogonz
Copy link
Collaborator

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.

@AlexJerabek
Copy link

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.

@octogonz
Copy link
Collaborator

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

@octogonz octogonz added the octogonz-parser-backlog Items for @octogonz to consider during the next iteration on the parser label Dec 9, 2019
@bajtos
Copy link

bajtos commented Jun 9, 2020

Would this work for merging interface docs into class docs? Using the example from microsoft/rushstack#1921:

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

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

@octogonz
Copy link
Collaborator

octogonz commented Jun 9, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
general discussion Not a bug or enhancement, just a discussion octogonz-parser-backlog Items for @octogonz to consider during the next iteration on the parser
Projects
None yet
Development

No branches or pull requests

4 participants