diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 718fd76625f..31c9acd8fb1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20009,14 +20009,20 @@ namespace ts { } function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { - const cls = getJSDocHost(node); - if (!isClassDeclaration(cls) && !isClassExpression(cls)) { - error(cls, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration); + const classLike = getJSDocHost(node); + if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { + error(classLike, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration); return; } + const augmentsTags = getAllJSDocTagsOfKind(classLike, SyntaxKind.JSDocAugmentsTag); + Debug.assert(augmentsTags.length > 0); + if (augmentsTags.length > 1) { + error(augmentsTags[1], Diagnostics.The_total_number_of_augments_and_extends_tags_allowed_for_a_single_class_declaration_is_at_most_1); + } + const name = getIdentifierFromEntityNameExpression(node.class.expression); - const extend = getClassExtendsHeritageClauseElement(cls); + const extend = getClassExtendsHeritageClauseElement(classLike); if (extend) { const className = getIdentifierFromEntityNameExpression(extend.expression); if (className && name.escapedText !== className.escapedText) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f3d6d4fcc47..64c4d99d349 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3527,6 +3527,10 @@ "category": "Error", "code": 8024 }, + "The total number of `@augments` and `@extends` tags allowed for a single class declaration is at most 1.": { + "category": "Error", + "code": 8025 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f0eb394adb7..825b310ddeb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4247,6 +4247,12 @@ namespace ts { return find(tags, doc => doc.kind === kind); } + /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray | undefined { + const tags = getJSDocTags(node); + return filter(tags, doc => doc.kind === kind); + } + } // Simple node tests of the form `node.kind === SyntaxKind.Foo`. diff --git a/tests/cases/fourslash/jsDocAugmentsAndExtends.ts b/tests/cases/fourslash/jsDocAugmentsAndExtends.ts new file mode 100644 index 00000000000..e76a617a3cf --- /dev/null +++ b/tests/cases/fourslash/jsDocAugmentsAndExtends.ts @@ -0,0 +1,50 @@ +/// + +// @allowJs: true +// @checkJs: true +// @Filename: dummy.js + +//// /** +//// * @augments {Thing} +//// * @extends {Thing} +//// */ +//// class MyStringThing extends Thing { +//// constructor() { +//// var x = this.mine; +//// x/**/; +//// } +//// } + +// @Filename: declarations.d.ts +//// declare class Thing { +//// mine: T; +//// } + +// if more than one tag is present, report an error and take the type of the first entry. + +goTo.marker(); +verify.quickInfoIs("(local var) x: number"); +verify.getSemanticDiagnostics( +`[ + { + "message": "The total number of \`@augments\` and \`@extends\` tags allowed for a single class declaration is at most 1.", + "start": 36, + "length": 24, + "category": "error", + "code": 8025 + }, + { + "message": "Constructors for derived classes must contain a \'super\' call.", + "start": 105, + "length": 59, + "category": "error", + "code": 2377 + }, + { + "message": "\'super\' must be called before accessing \'this\' in the constructor of a derived class.", + "start": 137, + "length": 4, + "category": "error", + "code": 17009 + } +]`); \ No newline at end of file