Skip to content

Commit 114b190

Browse files
committed
Copy type param descriptions to constructors
Resolves #3031
1 parent 3e70b65 commit 114b190

File tree

16 files changed

+376
-295
lines changed

16 files changed

+376
-295
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ title: Changelog
1313
API: Introduced `typeAnnotation` on `CommentTag`
1414
- Added `excludePrivateClassFields` option to hide `#private` members while allowing `private` members, #3017.
1515
- Added support for TypeScript's `@this` tag for JS files which describe `this` parameters, #3026.
16-
- Remove the `@jsx` tag from the list of additional block tags. TSDoc now directly supports this tag, #3035.
16+
- API: Re-introduced `relevanceBoost` on `DeclarationReflection` for plugin use, #3036.
1717

1818
## Bug Fixes
1919

2020
- Fixed conversion of auto-accessor types on properties with the `accessor` keyword, #3019.
2121
- Improved handling of HTML tags within headers for anchor generation, #3023.
2222
- Improved support for detecting destructured parameters and renaming them to the name used in the doc comment, #3026.
23+
- Constructor type parameters will now inherit their class's type parameter descriptions if not otherwise specified, #3031.
24+
- Fixed compatibility with `@microsoft/tsdoc-config` version 0.18.0, #3035.
25+
- Custom theme icons will now be used in the "On This Page" sidebar, #3039.
2326

2427
## v0.28.13 (2025-09-14)
2528

src/lib/converter/plugins/CommentPlugin.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ export class CommentPlugin extends ConverterComponent {
267267
_context: Context,
268268
reflection: TypeParameterReflection,
269269
) {
270+
if (reflection.comment) return;
271+
270272
const comment = reflection.parent?.comment;
271273
if (comment) {
272274
let tag = comment.getIdentifiedTag(reflection.name, "@typeParam");
@@ -286,6 +288,20 @@ export class CommentPlugin extends ConverterComponent {
286288
reflection.comment = new Comment(tag.content);
287289
reflection.comment.sourcePath = comment.sourcePath;
288290
removeIfPresent(comment.blockTags, tag);
291+
return;
292+
}
293+
}
294+
295+
// #3031 if this is a class constructor, also check for type parameters
296+
// that live on the class itself and potentially copy their comment.
297+
if (
298+
reflection.parent?.kindOf(ReflectionKind.ConstructorSignature) &&
299+
reflection.parent.parent?.kindOf(ReflectionKind.Constructor)
300+
) {
301+
const cls = reflection.parent.parent.parent as DeclarationReflection;
302+
const typeParam = cls.typeParameters?.find(param => param.name === reflection.name);
303+
if (typeParam?.comment) {
304+
reflection.comment = typeParam.comment.clone();
289305
}
290306
}
291307
}

src/lib/converter/symbols.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,15 @@ function convertClassOrInterface(
658658

659659
context.finalizeDeclarationReflection(reflection);
660660

661+
// Convert type parameters early so that we get access them when converting the constructors if any
662+
if (instanceType.typeParameters) {
663+
reflection.typeParameters = instanceType.typeParameters.map((param) => {
664+
const declaration = param.symbol.declarations?.[0];
665+
assert(declaration && ts.isTypeParameterDeclaration(declaration));
666+
return createTypeParamReflection(declaration, reflectionContext);
667+
});
668+
}
669+
661670
if (classDeclaration && reflection.kind === ReflectionKind.Class) {
662671
// Classes can have static props
663672
const staticType = context.checker.getTypeOfSymbolAtLocation(
@@ -712,15 +721,6 @@ function convertClassOrInterface(
712721
context.checker.getPropertiesOfType(instanceType),
713722
);
714723

715-
// And type arguments
716-
if (instanceType.typeParameters) {
717-
reflection.typeParameters = instanceType.typeParameters.map((param) => {
718-
const declaration = param.symbol.declarations?.[0];
719-
assert(declaration && ts.isTypeParameterDeclaration(declaration));
720-
return createTypeParamReflection(declaration, reflectionContext);
721-
});
722-
}
723-
724724
// Interfaces might also have call signatures
725725
// Classes might too, because of declaration merging
726726
context.checker

src/lib/models/DeclarationReflection.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export class DeclarationReflection extends ContainerReflection {
5050
/**
5151
* Precomputed boost for search results, may be less than 1 to de-emphasize this member in search results.
5252
* Does NOT include group/category values as they are computed when building the JS index.
53+
*
54+
* This is exposed purely for plugin use, see #3036 for details.
5355
*/
5456
relevanceBoost?: number;
5557

src/test/behavior.c2.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,4 +1509,21 @@ describe("Behavior Tests", () => {
15091509
`warn: Comment for E specifies @sortStrategy with "invalid2", which is an invalid sort strategy*`,
15101510
);
15111511
});
1512+
1513+
it("Handles different type parameter comments on class/constructor", () => {
1514+
const project = convert("ctorTypeParam");
1515+
1516+
const ctor = query(project, "Generic.constructor");
1517+
equal(ctor.signatures?.length, 2);
1518+
1519+
// @template on the class gets saved to class type parameters
1520+
equal(
1521+
Comment.combineDisplayParts(query(project, "Generic").typeParameters?.[0].comment?.summary),
1522+
"class docs",
1523+
);
1524+
// Custom @template tag for this constructor
1525+
equal(Comment.combineDisplayParts(ctor.signatures[0].typeParameters?.[0].comment?.summary), "ctor docs");
1526+
// Inherits description from class's @template
1527+
equal(Comment.combineDisplayParts(ctor.signatures[1].typeParameters?.[0].comment?.summary), "class docs");
1528+
});
15121529
});

0 commit comments

Comments
 (0)