-
Notifications
You must be signed in to change notification settings - Fork 607
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
[api-extractor] Add support for type alias types and type parameters #1317
Conversation
Looks awesome! :-) Thanks for implementing this! |
I found a few small issues I need to address before this might possibly be merged. |
It should be fixed now. |
I'm not certain if TypeParameters should be treated like Parameters (not themselves an ApiItem), or like a type declaration (which is an ApiItem). I wrote them as an ApiItem, in the event you need to perform a lookup when emitting yaml for DocFX, but I'm realizing that DocFX doesn't treat them as types. |
I'm pretty sure that |
I'll rework them back to be handled more like parameters are this weekend. |
Consider the following class: class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
} In the website docs, can you think of any cases where we would ever want If it was a hyperlink, should it simply take you to the page for the class declaration that introduced it? Would it be important to have an anchor to the table row for that particular type parameter? My feeling is that for a well-defined API nobody would really click these hyperlinks, they would just click on the class node itself because they know that's where the docs will be. But maybe there's some case I'm not thinking of. |
I'm more concerned about someone using a nontrivial name for a type parameter that also exists as a type to which lookups would be incorrectly pointed. |
I think you're referring to _linkToUidIfPossible() in API Documenter, that creates hyperlinks by matching symbol names. This was a temporary workaround. The hyperlinking is intended to be based on ExcerptTokenKind.Reference which is an .api.json field that will be calculated using the compiler's symbols, and be serialized as an unambiguous TSDoc declaration reference. This is a somewhat important feature that unfortunately didn't make the cut for API Extractor 7. There's a community PR #1005 that was working on implementing it. We really should help get it solved. That would eliminate any possibility of a type parameter being misinterpreted as a type, as far as I understand what you're saying. |
I was thinking of rewriting |
I can put together a Draft PR for my approach in the |
I created #1326 which includes an implementation of deeply linked types that should work with DocFX. |
apps/api-extractor-model/src/mixins/ApiTypeParameterListMixin.ts
Outdated
Show resolved
Hide resolved
apps/api-extractor-model/src/mixins/ApiTypeParameterListMixin.ts
Outdated
Show resolved
Hide resolved
No special meaning, it probably should be an error. |
export { | ||
IApiNameMixinOptions, | ||
ApiNameMixin | ||
} from './mixins/ApiNameMixin'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, somehow we forgot to export this
{ | ||
"packageName": "@microsoft/api-extractor", | ||
"comment": "Generate ApiTypeParameter entries and type alias types", | ||
"type": "patch" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed this to minor
. We're aiming to keep the versions the same between the packages.
(@iclanton maybe the best way would be to enable Rush lockstep versioning for api-extractor, api-extractor-model, and api-documenter.)
"changes": [ | ||
{ | ||
"packageName": "@microsoft/api-documenter", | ||
"comment": "Add support for emitting type parameters in the YamlDocumenter", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rbuckton Did you consider updating MarkdownDocumenter
? If you don't have time, I'm okay with merging this as-is and someone else from the community will likely come along and finish that piece.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't look at MarkdownEmitter
at all, but could consider it in a separate PR later if I have the time.
Just curious, why did you introduce |
/** | ||
* An {@link Excerpt} that describes the type of the alias. | ||
*/ | ||
public readonly aliasTypeExcerpt: Excerpt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the naming ...TypeAlias.aliasType...
to be somewhat uninformative. It seems to muddy the waters of whether the "alias" is the thing to the left or the right of the =
.
Could we rename this to originalTypeExcerpt
? Seems more straightforward to distinguish type TheAlias = TheOriginalType
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't align with the fact that type aliases can have type arguments, since it's not an "original" type, per se. If anything I would just rename it to typeExcerpt
(dropping the "alias" bit).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also mostly aligns with the fact that an ApiVariable
as a variableTypeExcerpt
. If I used that naming scheme it would have been TypeAlias.typeAliasType
. Of the three (aliasTypeExcerpt
, typeExcerpt
or typeAliasTypeExcerpt
), which would you prefer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But might we later introduce an excerpt corresponding to the Boxed<T>
part (to the left of the =
)? That thing is also a "type", at least to a layperson. It would be nice to have a more specific jargon. I can't think of anything very good, though. (alias "bodyExcerpt", alias "definitionExcerpt", ...)
If you can't think of anything better, type
is better than aliasType
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An ApiTypeAlias is also an ApiTypeParameterListMixin, so for type Boxed<T> = { box: T; };
, the <T>
will be found in ApiTypeAlias#typeParameters
and the { box: T; }
will be found in ApiTypeAlias#typeExcerpt
(or whatever its called). I think the whole of the declaration is covered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of the three (
aliasTypeExcerpt
,typeExcerpt
ortypeAliasTypeExcerpt
), which would you prefer?
Definitely typeExcerpt
of those three. Throwing in more copies of the words "alias" and/or "type" is definitely not clarifying anything heheh.
For type A = B;
I'm somewhat comfortable telling people that "A
is the alias, and B
is the type."
I plan to use it in a later PR, but there's nothing stopping other tooling beyond The main reason its not used is that DocFX doesn't understand type aliases without a custom template. I'm using a custom template for |
* | ||
* @remarks | ||
* In the example below, the `aliasTypeExcerpt` would correspond to the subexpression | ||
* `T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW I found it interesting that the ;
is part of the excerpt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it interesting that a number of excerpts include an empty " "
token at the end.
Actually, I already had this work in a stash. I can just add it to this PR. |
@rbuckton I finished reviewing your PR. Overall it looks good. There's one remaining thing I want to investigate myself (whether we can easily use But otherwise let me know when you're done with any further changes you want to make, and then we can merge it. Thanks again for implementing this feature -- it's very cool! |
I should have an update with the name change for |
On second thought, I'll postpone the typealias/variable YAML emit for a later PR and will just push up the name change in a moment. |
Should be good to go once the build finishes. |
I'll tackle this as a separate PR. It needs to add a context parameter to every deserialization function, and there are a bunch of them. |
Either that, or handle the schema translation in |
It's tempting to simply report an error if the schema version is different, i.e. provide no backwards compatibility at all, and see if anyone complains about it. If we allow loading of old .api.json files, the rendered website is going to be wrong (e.g. the type parameters will be simply missing for certain packages, but displayed for others). This could cause weird breaks for a custom tool someone wrote that that somehow depends on the new feature. It could possibly be a more troublesome experience for users than forcing them to upgrade and rebuild. Better to discuss it as a separate PR, though. |
Leaving this as a draft PR for now as I'm just curious what you think about this approach. This adds support for
TypeParameterDeclaration
lists and the type for aTypeAliasDeclaration
, which currently aren't included in the api-extractor output. This does not add support in api-documenter yet, though type parameters are supported by docfx and Type Aliases could also be supported in docfx via a custom theme.