Skip to content

Commit 1db7623

Browse files
authored
Merge pull request #18706 from aozgaa/dev/aozgaa/JsDocExtendsSupport
support @extends in jsdoc
2 parents 661ecc2 + 5f3d6e7 commit 1db7623

File tree

10 files changed

+96
-5
lines changed

10 files changed

+96
-5
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20032,14 +20032,20 @@ namespace ts {
2003220032
}
2003320033

2003420034
function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void {
20035-
const cls = getJSDocHost(node);
20036-
if (!isClassDeclaration(cls) && !isClassExpression(cls)) {
20037-
error(cls, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration);
20035+
const classLike = getJSDocHost(node);
20036+
if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) {
20037+
error(classLike, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration);
2003820038
return;
2003920039
}
2004020040

20041+
const augmentsTags = getAllJSDocTagsOfKind(classLike, SyntaxKind.JSDocAugmentsTag);
20042+
Debug.assert(augmentsTags.length > 0);
20043+
if (augmentsTags.length > 1) {
20044+
error(augmentsTags[1], Diagnostics.Class_declarations_cannot_have_more_than_one_augments_or_extends_tag);
20045+
}
20046+
2004120047
const name = getIdentifierFromEntityNameExpression(node.class.expression);
20042-
const extend = getClassExtendsHeritageClauseElement(cls);
20048+
const extend = getClassExtendsHeritageClauseElement(classLike);
2004320049
if (extend) {
2004420050
const className = getIdentifierFromEntityNameExpression(extend.expression);
2004520051
if (className && name.escapedText !== className.escapedText) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3531,6 +3531,10 @@
35313531
"category": "Error",
35323532
"code": 8024
35333533
},
3534+
"Class declarations cannot have more than one `@augments` or `@extends` tag.": {
3535+
"category": "Error",
3536+
"code": 8025
3537+
},
35343538
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
35353539
"category": "Error",
35363540
"code": 9002

src/compiler/parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6373,6 +6373,7 @@ namespace ts {
63736373
if (tagName) {
63746374
switch (tagName.escapedText) {
63756375
case "augments":
6376+
case "extends":
63766377
tag = parseAugmentsTag(atToken, tagName);
63776378
break;
63786379
case "class":

src/compiler/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,6 +2159,10 @@ namespace ts {
21592159
kind: SyntaxKind.JSDocTag;
21602160
}
21612161

2162+
/**
2163+
* Note that `@extends` is a synonym of `@augments`.
2164+
* Both tags are represented by this interface.
2165+
*/
21622166
export interface JSDocAugmentsTag extends JSDocTag {
21632167
kind: SyntaxKind.JSDocAugmentsTag;
21642168
class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression };

src/compiler/utilities.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4241,6 +4241,12 @@ namespace ts {
42414241
return find(tags, doc => doc.kind === kind);
42424242
}
42434243

4244+
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
4245+
export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray<JSDocTag> | undefined {
4246+
const tags = getJSDocTags(node);
4247+
return filter(tags, doc => doc.kind === kind);
4248+
}
4249+
42444250
}
42454251

42464252
// Simple node tests of the form `node.kind === SyntaxKind.Foo`.

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,10 @@ declare namespace ts {
14421442
interface JSDocUnknownTag extends JSDocTag {
14431443
kind: SyntaxKind.JSDocTag;
14441444
}
1445+
/**
1446+
* Note that `@extends` is a synonym of `@augments`.
1447+
* Both tags are represented by this interface.
1448+
*/
14451449
interface JSDocAugmentsTag extends JSDocTag {
14461450
kind: SyntaxKind.JSDocAugmentsTag;
14471451
class: ExpressionWithTypeArguments & {
@@ -2866,6 +2870,8 @@ declare namespace ts {
28662870
function getJSDocReturnType(node: Node): TypeNode | undefined;
28672871
/** Get all JSDoc tags related to a node, including those on parent nodes. */
28682872
function getJSDocTags(node: Node): ReadonlyArray<JSDocTag> | undefined;
2873+
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
2874+
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray<JSDocTag> | undefined;
28692875
}
28702876
declare namespace ts {
28712877
function isNumericLiteral(node: Node): node is NumericLiteral;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,10 @@ declare namespace ts {
14421442
interface JSDocUnknownTag extends JSDocTag {
14431443
kind: SyntaxKind.JSDocTag;
14441444
}
1445+
/**
1446+
* Note that `@extends` is a synonym of `@augments`.
1447+
* Both tags are represented by this interface.
1448+
*/
14451449
interface JSDocAugmentsTag extends JSDocTag {
14461450
kind: SyntaxKind.JSDocAugmentsTag;
14471451
class: ExpressionWithTypeArguments & {
@@ -2921,6 +2925,8 @@ declare namespace ts {
29212925
function getJSDocReturnType(node: Node): TypeNode | undefined;
29222926
/** Get all JSDoc tags related to a node, including those on parent nodes. */
29232927
function getJSDocTags(node: Node): ReadonlyArray<JSDocTag> | undefined;
2928+
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
2929+
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray<JSDocTag> | undefined;
29242930
}
29252931
declare namespace ts {
29262932
function isNumericLiteral(node: Node): node is NumericLiteral;

tests/cases/fourslash/jsDocAugments.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,3 @@
2020

2121
goTo.marker();
2222
verify.quickInfoIs("(local var) x: string");
23-
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// @allowJs: true
4+
// @checkJs: true
5+
// @Filename: dummy.js
6+
7+
//// /**
8+
//// * @augments {Thing<number>}
9+
//// * @extends {Thing<string>}
10+
//// */
11+
//// class MyStringThing extends Thing {
12+
//// constructor() {
13+
//// super();
14+
//// var x = this.mine;
15+
//// x/**/;
16+
//// }
17+
//// }
18+
19+
// @Filename: declarations.d.ts
20+
//// declare class Thing<T> {
21+
//// mine: T;
22+
//// }
23+
24+
// if more than one tag is present, report an error and take the type of the first entry.
25+
26+
goTo.marker();
27+
verify.quickInfoIs("(local var) x: number");
28+
verify.getSemanticDiagnostics(
29+
`[
30+
{
31+
"message": "Class declarations cannot have more than one \`@augments\` or \`@extends\` tag.",
32+
"start": 36,
33+
"length": 24,
34+
"category": "error",
35+
"code": 8025
36+
}
37+
]`);

tests/cases/fourslash/jsDocExtends.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// @allowJs: true
4+
// @Filename: dummy.js
5+
6+
//// /**
7+
//// * @extends {Thing<string>}
8+
//// */
9+
//// class MyStringThing extends Thing {
10+
//// constructor() {
11+
//// var x = this.mine;
12+
//// x/**/;
13+
//// }
14+
//// }
15+
16+
// @Filename: declarations.d.ts
17+
//// declare class Thing<T> {
18+
//// mine: T;
19+
//// }
20+
21+
goTo.marker();
22+
verify.quickInfoIs("(local var) x: string");

0 commit comments

Comments
 (0)