diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f2c85d7a0d94..7a361b09869d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3796,6 +3796,16 @@ namespace ts { } baseType = getReturnTypeOfSignature(constructors[0]); } + + // In a JS file, you can use the @augments jsdoc tag to specify a base type with type parameters + const valueDecl = type.symbol.valueDeclaration; + if (valueDecl && isInJavaScriptFile(valueDecl)) { + const augTag = getJSDocAugmentsTag(type.symbol.valueDeclaration); + if (augTag) { + baseType = getTypeFromTypeNode(augTag.typeExpression.type); + } + } + if (baseType === unknownType) { return; } @@ -3804,7 +3814,7 @@ namespace ts { return; } if (type === baseType || hasBaseType(baseType, type)) { - error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, + error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); return; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d286a9d9dac10..0ce21b2d9fed9 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -418,6 +418,8 @@ namespace ts { return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocTypeTag: return visitNode(cbNode, (node).typeExpression); + case SyntaxKind.JSDocAugmentsTag: + return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: @@ -6426,6 +6428,9 @@ namespace ts { let tag: JSDocTag; if (tagName) { switch (tagName.text) { + case "augments": + tag = parseAugmentsTag(atToken, tagName); + break; case "param": tag = parseParamTag(atToken, tagName); break; @@ -6642,6 +6647,16 @@ namespace ts { return finishNode(result); } + function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag { + const typeExpression = tryParseTypeExpression(); + + const result = createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos); + result.atToken = atToken; + result.tagName = tagName; + result.typeExpression = typeExpression; + return finishNode(result); + } + function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag { const typeExpression = tryParseTypeExpression(); skipWhitespace(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2cbace563a0e1..176176370e072 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -349,6 +349,7 @@ namespace ts { JSDocThisType, JSDocComment, JSDocTag, + JSDocAugmentsTag, JSDocParameterTag, JSDocReturnTag, JSDocTypeTag, @@ -1989,6 +1990,11 @@ namespace ts { kind: SyntaxKind.JSDocTag; } + export interface JSDocAugmentsTag extends JSDocTag { + kind: SyntaxKind.JSDocAugmentsTag; + typeExpression: JSDocTypeExpression; + } + export interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e313f983fad37..363ddf0de801c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1539,6 +1539,10 @@ namespace ts { return tag && tag.typeExpression && tag.typeExpression.type; } + export function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag { + return getFirstJSDocTag(node, SyntaxKind.JSDocAugmentsTag) as JSDocAugmentsTag; + } + export function getJSDocReturnTag(node: Node): JSDocReturnTag { return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag; } diff --git a/tests/cases/fourslash/jsDocAugments.ts b/tests/cases/fourslash/jsDocAugments.ts new file mode 100644 index 0000000000000..24458c529fb92 --- /dev/null +++ b/tests/cases/fourslash/jsDocAugments.ts @@ -0,0 +1,23 @@ +/// + +// @allowJs: true +// @Filename: dummy.js + +//// /** +//// * @augments {Thing} +//// */ +//// class MyStringThing extends Thing { +//// constructor() { +//// var x = this.mine; +//// x/**/; +//// } +//// } + +// @Filename: declarations.d.ts +//// declare class Thing { +//// mine: T; +//// } + +goTo.marker(); +verify.quickInfoIs("(local var) x: string"); +