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

How to document each type in `export type Foo = 'foo1' | 'foo2'? #2585

Closed
ibc opened this issue Jun 3, 2024 · 12 comments
Closed

How to document each type in `export type Foo = 'foo1' | 'foo2'? #2585

ibc opened this issue Jun 3, 2024 · 12 comments
Labels
enhancement Improved functionality
Milestone

Comments

@ibc
Copy link

ibc commented Jun 3, 2024

Question

Basically I need those 'foo1' and 'foo2'. being documented:

/**
 * Foo valid values.
 */
export type Foo =
	/**
	 * Doc of foo1.
	 */
	| 'foo1'
	/**
	 * Doc of foo2.
	 */
	| 'foo2';

However generated docs look like this (documentation about 'foo1' and 'foo2' is not parsed/included):

CleanShot 2024-06-03 at 17 54 01@2x

I know that I could make it "work" by doing this complex thing:

/**
 * Doc of foo1.
 */
export type Foo1 = 'foo1';

/**
 * Doc of foo2.
 */
export type Foo2 = 'foo2';

/**
 * Foo valid values.
 */
export type Foo = Foo1 | Foo2;

It would produce this:

CleanShot 2024-06-03 at 17 55 23@2x
CleanShot 2024-06-03 at 17 55 41@2x
CleanShot 2024-06-03 at 17 55 49@2x

But this is definitely too complex and definitely not desirable at all. Having to click on each type to see how a string looks is too much. Do I miss something?

@ibc ibc added the question Question about functionality label Jun 3, 2024
@Oblarg
Copy link

Oblarg commented Jun 5, 2024

You cannot; this is a tsdoc limitation.

microsoft/tsdoc#164

@ibc
Copy link
Author

ibc commented Jun 5, 2024

Understood. Funny thing is that in that issue I found this comment :)

CleanShot 2024-06-05 at 15 44 41@2x

microsoft/tsdoc#164 (comment)

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Jun 6, 2024

My recommendation for this today is to use @enum , and export an enum-object.

/**
 * This will be displayed as an enumeration.
 * @enum
 */
export const Foo = {
    /**
     * Doc comments may be included here.
     */
    foo1: "foo2",
    foo2: "foo2",
} as const;

// This will be hidden, but produces the same type as in the OP
export type Foo = typeof Foo[keyof typeof Foo];

I looked at supporting this in TypeDoc, but doing so requires an unfortunately large number of hacky assumptions, and essentially requires re-implementing (parts of) a lexer for TypeScript. Without TypeScript support I find the @enum version is better for downstream consumers anyways.

@ibc
Copy link
Author

ibc commented Jun 6, 2024

Thanks. But not sure this trick will be useful because my real case is more complex. I would need to declare a const Foo like this:

const MyEvent = {
  /**
   * Doc of event "foo1" with clickable links
   * to AnotherType2 and AnotherType3.
   */
  "foo1": AnotherTypeA<[param1: AnotherType1, param2: AnotherType2]>,
   "foo2": etc
} as const;

I am 99% sure that the export type MyEvent you suggested won't render clickable links to those AnotherTypeN types because an enum is supposed to have numbers of string as values and not complex types. I may be wrong. Will test it on next Monday.

Thanks a lot.

@Oblarg
Copy link

Oblarg commented Jun 6, 2024

This is a really big missing feature in TypeScript/TSDoc.

So far my approach has been to ban inline type definitions in unions beyond a very rudimentary base complexity, and settle with requiring our users to click through to the component definition. Together with enforcing that "primitive" object literal types be tagged with @interface (I do not use interface in code, typically, because tooltip renderers can't expand it), the result ends up pretty easy to follow:

image

Anything green is a single "primitive" event; anything in red is still structured/algebraic.

Huge credit to @Gerrit0; this is a lot better than it was a year ago, even with the current limitations.

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Jun 9, 2024

I spent a bit of time poking around in https://ts-ast-viewer.com, and it looks like supporting this (specifically for type alias unions) is slightly less messy than I remember... still not great, but it looks possible without an unacceptably large amount of hackery.

Gerrit0 added a commit that referenced this issue Jun 9, 2024
@Gerrit0
Copy link
Collaborator

Gerrit0 commented Jun 9, 2024

... I didn't expect that to take ~4 hours to implement, but there were lots of rabbit holes. In 0.26.0-beta.3 (publishing sometime today)

/**
 * Represents a parsed piece of a comment.
 * @category Comments
 * @see {@link JSONOutput.CommentDisplayPart}
 */
export type CommentDisplayPart =
    /**
     * Represents a plain text portion of the comment, may contain markdown
     */
    | { kind: "text"; text: string }
    /**
     * Represents a code block separated out form the plain text entry so
     * that TypeDoc knows to skip it when parsing relative links and inline tags.
     **/
    | { kind: "code"; text: string }
    /**
     * Represents an inline tag like `{@link Foo}`
     */
    | InlineTagDisplayPart
    /**
     * Represents a reference to a path relative to where the comment resides.
     * This is used to detect and copy relative image links.
     */
    | RelativeLinkDisplayPart;

is now rendered as:

image

@Gerrit0 Gerrit0 added enhancement Improved functionality and removed question Question about functionality labels Jun 9, 2024
@Gerrit0 Gerrit0 mentioned this issue Jun 9, 2024
7 tasks
@ibc
Copy link
Author

ibc commented Jun 10, 2024

Yes, indeed I was already using a type with many | as a workaround for it, but looks a bit cumbersome:

/**
 * Events emitted by the {@link Notifier} class.
 */
export type NotifierEvents =
  | NotifierWaitingForJoinApprovalEvent
  | NotifierYouHaveBeenAuthorizedToJoinEvent
  | NotifierAttendeeHasJoinedEvent
  | NotifierAttendeeHasLeftEvent
  | NotifierAttendeeJoinRequestEvent
  | NotifierMediaDeviceErrorEvent
  | NotifierNoMediaDeviceEvent
  | NotifierAudioInputDeviceChangedEvent
  | NotifierAudioOutputDeviceChangedEvent
  | NotifierVideoInputDeviceChangedEvent
  | NotifierCameraFilterEvent
  | NotifierMicrophoneDisconnectedEvent
  | NotifierCameraDisconnectedEvent
  | NotifierRemoteMutedEvent;

CleanShot 2024-06-10 at 10 48 01@2x

Definitely I wanted to avoid this look and feel in which the user needs to click on every item to see its definition. Now I understand that this is not even supported by core tsdoc so will live with it.

Thanks a lot.

NOTE: Feel free to close this issue if you think that it doesn't make sense to keep it open.

@Oblarg
Copy link

Oblarg commented Jun 10, 2024

@Gerrit0 thanks a ton for addressing this; it'll be super useful.

I use algebraic types a lot, and documenting them has been the hardest problem by far. Tools like this matter.

@Gerrit0 Gerrit0 added this to the v0.26.0 milestone Jun 12, 2024
@Gerrit0
Copy link
Collaborator

Gerrit0 commented Jun 16, 2024

Fixed with 0.26, which is releasing 2024/06/21

@Gerrit0 Gerrit0 closed this as completed Jun 16, 2024
@Oblarg
Copy link

Oblarg commented Jun 16, 2024

@Gerrit0 you rule! will immediately be bumping our version upon release.

@ibc
Copy link
Author

ibc commented Jun 17, 2024

Tested. This is amazing. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improved functionality
Projects
None yet
Development

No branches or pull requests

3 participants