[api-extractor] Support docs/trimming for members of a literal union type alias
AJIXuMuK opened this issue · comments
Summary
Release tags (@alpha
, @beta
, @internal
, @public
) are great and allow having flexibility in API releases.
One of the features we would benefit from is the ability to specify the release tags for union types members.
Details
For example, we may have an existing API like
/**
* @public
*/
export type IconSize = 'small' | 'medium';
We want to test a new large
size as beta
.
So we would like to be able to do something like:
/**
* @public
*/
export type IconSize =
| 'small'
| 'medium'
/**
* @beta
*/
| 'large';
and allow API Extractor to create different definitions for public and beta releases.
This behavior can be "opt-in" only: for example, we may need to specify extra tsdoc tag to the type to process the type in a new way:
/**
* @public
* @formedType
*/
export type IconSize =
| 'small'
| 'medium'
/**
* @beta
*/
| 'large';
The same is applicable for union of interface:
/**
* @public
* @formedType
*/
export type ButtonType =
| IPrimaryButton
| ISecondaryButton
/**
* @beta
*/
| IOutlineButton;
Standard questions
Please answer these questions to help us investigate your issue more quickly:
Question | Answer |
---|---|
@microsoft/api-extractor version? |
latest |
Operating system? | any |
API Extractor scenario? | rollups (.d.ts) --> |
Would you consider contributing a PR? | maybe |
TypeScript compiler version? | 5.3.3 |
Node.js version (node -v )? |
18.20.2 |
This feature request is closely related to #3002 which asks for documenting/trimming members of a type
alias that mimics an interface
.
In general, type
can produce arbitrarily complex expressions that are difficult for API Extractor to model as "API items" the documentation website, and are not safe for .d.ts trimming. (Specifically, the concern is that trimming members may produce .d.ts rollups that fail to compile, and it can be difficult to detect or correct such cases, due to the expressiveness/flexibility of the type system.)
But in both cases, type
is being used for a restricted pattern that is very similar to an already supported API item kind:
#4699 involves a type
that looks like ApiEnum:
/**
* The icon size.
* @public
*/
export type IconSize =
/**
* 16x16 pixels
*/
'small' |
/**
* 32x32 pixels
* @beta
*/
'medium';
#3002 involves a type
that looks like ApiInterface:
export type Book = {
/** The title */
title: string;
/** The description */
description: string;
};
Thus, these particular patterns should be relatively well-behaved and straightforward to incorporate into the API item model. We could try to automatically detect them, but I think this would give a confusing experience. A seemingly small modification such as export type IconSize = 'small' | 'medium' | UnsupportedThing
could suddenly cause the API docs for 'small'
and 'medium'
to disappear from the website, because the expected pattern is no longer matched.
Proposed design
Let's call the above patterns formed types which means a type
that follows a predefined stereotypical form. The first example is a formed enum, and the second example is a formed interface, and in the future we could support more such patterns.
In this proposal, the user must declare the form explicitly, using new TSDoc tags such as @formedEnum
or @formedInterface
. For example:
/**
* The icon size.
* @formedEnum
* @public
*/
export type IconSize =
/** 16x16 pixels */
'small' |
/** 32x32 pixels */
'medium';
/**
* @formedInterface
* @public
*/
export type Book = {
/** The title */
title: string;
/** The description */
description: string;
};
With these tags, API Extractor will validate that the type
conforms to the supported structure, and report an error message if not. And then members such as 'small'
or title
will become ApiItem
objects in the model, with full support for .d.ts trimming and structured website documentation, just like ApiEnumMember
or ApiProperty
.
Whereas if you omit the @formedEnum
and @formedInterface
tags, then these declarations would instead become an ApiTypeAlias
whose members are just a blob of text in the signature without any special interpretation.
Implementation
The @formedEnum
and @formedInterface
features involve considerations and algorithms, so let's continue to track them using separate GitHub issues.
This issue #4699 I think is the easier one to start with. We've already confirmed that the TypeScript compiler correctly propagates all the /** */
comments to the .d.ts file. One slightly tricky aspect is the position of |
, for example...
export type IconSize =
/** 16x16 pixels */
'small' |
/** 32x32 pixels */
'medium';
...versus...
export type IconSize =
/** 16x16 pixels */
'small'
/** 32x32 pixels */
| 'medium';
...and what would we do with...
export type IconSize =
/** 16x16 pixels */
'small'
/** A */
|
/** B */
'medium';
We also need to decide whether to reuse ApiEnum
and ApiEnumMember
, or else define new model classes such as ApiFormedEnum
and ApiFormedEnumMember
. I don't have a strong opinion about this.