Skip to content

Commit

Permalink
Add support for @interface
Browse files Browse the repository at this point in the history
Closes #1519
  • Loading branch information
Gerrit0 committed Mar 25, 2023
1 parent e370aab commit 41721f9
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

### Features

- Added support for `@interface` on type aliases to tell TypeDoc to convert the fully resolved type as an interface, #1519
- Added support for `@prop`/`@property` to specify documentation for a child property of a symbol, intended for use with `@interface`.
- Plugins may now return a `Promise<void>` from their `load` function, #185.
- TypeDoc now supports plugins written with ESM, #1635.
- Added `Renderer.preRenderAsyncJobs` and `Renderer.postRenderAsyncJobs`, which may be used by plugins to perform async processing for rendering, #185.
Expand Down
5 changes: 4 additions & 1 deletion src/lib/converter/comments/discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ const wantedKinds: Record<ReflectionKind, ts.SyntaxKind[]> = {
ts.SyntaxKind.ClassDeclaration,
ts.SyntaxKind.BindingElement,
],
[ReflectionKind.Interface]: [ts.SyntaxKind.InterfaceDeclaration],
[ReflectionKind.Interface]: [
ts.SyntaxKind.InterfaceDeclaration,
ts.SyntaxKind.TypeAliasDeclaration,
],
[ReflectionKind.Constructor]: [ts.SyntaxKind.Constructor],
[ReflectionKind.Property]: variablePropertyKinds,
[ReflectionKind.Method]: [
Expand Down
22 changes: 22 additions & 0 deletions src/lib/converter/plugins/CommentPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export class CommentPlugin extends ConverterComponent {
comment.removeModifier("@namespace");
}

if (reflection.kindOf(ReflectionKind.Interface)) {
comment.removeModifier("@interface");
}

if (comment.hasModifier("@private")) {
reflection.setFlag(ReflectionFlag.Private);
if (reflection.kindOf(ReflectionKind.CallSignature)) {
Expand Down Expand Up @@ -344,6 +348,7 @@ export class CommentPlugin extends ConverterComponent {
}

mergeSeeTags(reflection.comment);
movePropertyTags(reflection.comment, reflection);
}

if (!(reflection instanceof DeclarationReflection)) {
Expand Down Expand Up @@ -582,6 +587,23 @@ function moveNestedParamTags(comment: Comment, parameter: ParameterReflection) {
parameter.type?.visit(visitor);
}

function movePropertyTags(comment: Comment, container: Reflection) {
const propTags = comment.blockTags.filter(
(tag) => tag.tag === "@prop" || tag.tag === "@property"
);

for (const prop of propTags) {
if (!prop.name) continue;

const child = container.getChildByName(prop.name);
if (child) {
child.comment = new Comment(
Comment.cloneDisplayParts(prop.content)
);
}
}
}

function mergeSeeTags(comment: Comment) {
const see = comment.getTags("@see");

Expand Down
51 changes: 51 additions & 0 deletions src/lib/converter/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,15 @@ function convertTypeAlias(
assert(declaration);

if (ts.isTypeAliasDeclaration(declaration)) {
if (symbol.getJsDocTags().some((tag) => tag.name === "interface")) {
return convertTypeAliasAsInterface(
context,
symbol,
exportSymbol,
declaration
);
}

const reflection = context.createDeclarationReflection(
ReflectionKind.TypeAlias,
symbol,
Expand Down Expand Up @@ -351,6 +360,48 @@ function convertTypeAlias(
}
}

function convertTypeAliasAsInterface(
context: Context,
symbol: ts.Symbol,
exportSymbol: ts.Symbol | undefined,
declaration: ts.TypeAliasDeclaration
) {
const reflection = context.createDeclarationReflection(
ReflectionKind.Interface,
symbol,
exportSymbol
);
context.finalizeDeclarationReflection(reflection);
const rc = context.withScope(reflection);

const type = context.checker.getTypeAtLocation(declaration);

// Interfaces have properties
convertSymbols(rc, type.getProperties());

// And type arguments
if (declaration.typeParameters) {
reflection.typeParameters = declaration.typeParameters.map((param) => {
const declaration = param.symbol?.declarations?.[0];
assert(declaration && ts.isTypeParameterDeclaration(declaration));
return createTypeParamReflection(declaration, rc);
});
}

// And maybe call signatures
context.checker
.getSignaturesOfType(type, ts.SignatureKind.Call)
.forEach((sig) =>
createSignature(rc, ReflectionKind.CallSignature, sig, symbol)
);

// And maybe constructor signatures
convertConstructSignatures(rc, symbol);

// And finally, index signatures
convertIndexSignature(rc, symbol);
}

function convertFunctionOrMethod(
context: Context,
symbol: ts.Symbol,
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils/options/tsdoc-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ export const modifierTags = [
"@event",
"@overload",
"@namespace",
"@interface",
] as const;
43 changes: 29 additions & 14 deletions src/test/behaviorTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -743,20 +743,6 @@ export const behaviorTests: {
);
},

seeTags(project) {
const foo = query(project, "foo");
equal(
Comment.combineDisplayParts(foo.comment?.getTag("@see")?.content),
" - Double tag\n - Second tag\n"
);

const bar = query(project, "bar");
equal(
Comment.combineDisplayParts(bar.comment?.getTag("@see")?.content),
"Single tag"
);
},

_searchCategoryBoosts(app) {
app.options.setValue("searchCategoryBoosts", {
Cat0: 0,
Expand Down Expand Up @@ -803,4 +789,33 @@ export const behaviorTests: {
);
logger.expectNoOtherMessages();
},

seeTags(project) {
const foo = query(project, "foo");
equal(
Comment.combineDisplayParts(foo.comment?.getTag("@see")?.content),
" - Double tag\n - Second tag\n"
);

const bar = query(project, "bar");
equal(
Comment.combineDisplayParts(bar.comment?.getTag("@see")?.content),
"Single tag"
);
},

typeAliasInterface(project) {
const bar = query(project, "Bar");
equal(bar.kind, ReflectionKind.Interface);
equal(
bar.children?.map((c) => c.name),
["a", "b"]
);

const comments = [bar, bar.children[0], bar.children[1]].map((r) =>
Comment.combineDisplayParts(r.comment?.summary)
);

equal(comments, ["Bar docs", "Bar.a docs", "Foo.b docs"]);
},
};
22 changes: 22 additions & 0 deletions src/test/converter2/behavior/typeAliasInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Foo docs
*/
export type Foo = {
/**
* Foo.a docs
*/
a: 123;
/**
* Foo.b docs
*/
b: 456;
};

/**
* Bar docs
* @property a Bar.a docs
* @interface
*/
export type Bar = {
[K in keyof Foo]: string;
};
4 changes: 4 additions & 0 deletions tsdoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
{
"tagName": "@namespace",
"syntaxKind": "modifier"
},
{
"tagName": "@interface",
"syntaxKind": "modifier"
}
]
}

0 comments on commit 41721f9

Please sign in to comment.