diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d89247b70f978..4337d1fb79b5c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4882,7 +4882,16 @@ namespace ts { } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { - return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); + const decl = type.symbol.valueDeclaration; + if (isInJavaScriptFile(decl)) { + // Prefer an @augments tag because it may have type parameters. + const tag = getJSDocAugmentsTag(decl); + if (tag) { + return tag.class; + } + } + + return getClassExtendsHeritageClauseElement(decl); } function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] { @@ -4986,15 +4995,6 @@ 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 && augTag.typeExpression && augTag.typeExpression.type) { - baseType = getTypeFromTypeNode(augTag.typeExpression.type); - } - } - if (baseType === unknownType) { return; } @@ -5003,7 +5003,7 @@ namespace ts { return; } if (type === baseType || hasBaseType(baseType, type)) { - error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, + error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); return; } @@ -19789,6 +19789,38 @@ 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); + return; + } + + const name = getIdentifierFromEntityNameExpression(node.class.expression); + const extend = getClassExtendsHeritageClauseElement(cls); + if (extend) { + const className = getIdentifierFromEntityNameExpression(extend.expression); + if (className && name.escapedText !== className.escapedText) { + error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, + unescapeLeadingUnderscores(name.escapedText), + unescapeLeadingUnderscores(className.escapedText)); + } + } + } + + function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier; + function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined; + function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined { + switch (node.kind) { + case SyntaxKind.Identifier: + return node as Identifier; + case SyntaxKind.PropertyAccessExpression: + return (node as PropertyAccessExpression).name; + default: + return undefined; + } + } + function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void { checkDecorators(node); checkSignatureDeclaration(node); @@ -22483,6 +22515,8 @@ namespace ts { case SyntaxKind.ParenthesizedType: case SyntaxKind.TypeOperator: return checkSourceElement((node).type); + case SyntaxKind.JSDocAugmentsTag: + return checkJSDocAugmentsTag(node as JSDocAugmentsTag); case SyntaxKind.JSDocTypedefTag: return checkJSDocTypedefTag(node as JSDocTypedefTag); case SyntaxKind.JSDocParameterTag: diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2a8b74d64bdf2..442ed858fffa6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3511,6 +3511,14 @@ "category": "Error", "code": 8021 }, + "JSDoc '@augments' is not attached to a class declaration.": { + "category": "Error", + "code": 8022 + }, + "JSDoc '@augments {0}' does not match the 'extends {1}' clause.": { + "category": "Error", + "code": 8023 + }, "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/parser.ts b/src/compiler/parser.ts index e2eb3efc01224..baa89c6133534 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -424,7 +424,7 @@ namespace ts { case SyntaxKind.JSDocTypeTag: return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocAugmentsTag: - return visitNode(cbNode, (node).typeExpression); + return visitNode(cbNode, (node).class); case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNode, cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: @@ -5624,13 +5624,16 @@ namespace ts { function parseExpressionWithTypeArguments(): ExpressionWithTypeArguments { const node = createNode(SyntaxKind.ExpressionWithTypeArguments); node.expression = parseLeftHandSideExpressionOrHigher(); - if (token() === SyntaxKind.LessThanToken) { - node.typeArguments = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); - } - + node.typeArguments = tryParseTypeArguments(); return finishNode(node); } + function tryParseTypeArguments(): NodeArray | undefined { + return token() === SyntaxKind.LessThanToken + ? parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken) + : undefined; + } + function isHeritageClause(): boolean { return token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword; } @@ -6604,15 +6607,36 @@ namespace ts { } function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag { - const typeExpression = parseJSDocTypeExpression(/*requireBraces*/ true); - const result = createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; - result.typeExpression = typeExpression; + result.class = parseExpressionWithTypeArgumentsForAugments(); return finishNode(result); } + function parseExpressionWithTypeArgumentsForAugments(): ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression } { + const usedBrace = parseOptional(SyntaxKind.OpenBraceToken); + const node = createNode(SyntaxKind.ExpressionWithTypeArguments) as ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; + node.expression = parsePropertyAccessEntityNameExpression(); + node.typeArguments = tryParseTypeArguments(); + const res = finishNode(node); + if (usedBrace) { + parseExpected(SyntaxKind.CloseBraceToken); + } + return res; + } + + function parsePropertyAccessEntityNameExpression() { + let node: Identifier | PropertyAccessEntityNameExpression = parseJSDocIdentifierName(/*createIfMissing*/ true); + while (token() === SyntaxKind.DotToken) { + const prop: PropertyAccessEntityNameExpression = createNode(SyntaxKind.PropertyAccessExpression, node.pos) as PropertyAccessEntityNameExpression; + prop.expression = node; + prop.name = parseJSDocIdentifierName(); + node = finishNode(prop); + } + return node; + } + function parseClassTag(atToken: AtToken, tagName: Identifier): JSDocClassTag { const tag = createNode(SyntaxKind.JSDocClassTag, atToken.pos); tag.atToken = atToken; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index c9c14198279a2..b19a14663285d 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1856,6 +1856,12 @@ namespace ts { case CharacterCodes.closeBracket: pos++; return token = SyntaxKind.CloseBracketToken; + case CharacterCodes.lessThan: + pos++; + return token = SyntaxKind.LessThanToken; + case CharacterCodes.greaterThan: + pos++; + return token = SyntaxKind.GreaterThanToken; case CharacterCodes.equals: pos++; return token = SyntaxKind.EqualsToken; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7a120064a64cb..1f27edcaf89d9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2161,7 +2161,7 @@ namespace ts { export interface JSDocAugmentsTag extends JSDocTag { kind: SyntaxKind.JSDocAugmentsTag; - typeExpression: JSDocTypeExpression; + class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; } export interface JSDocClassTag extends JSDocTag { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index cc7002ca0590c..fde149b7df171 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1581,8 +1581,7 @@ namespace ts { return undefined; } const name = node.name.escapedText; - Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment); - const func = node.parent!.parent!; + const func = getJSDocHost(node); if (!isFunctionLike(func)) { return undefined; } @@ -1591,6 +1590,11 @@ namespace ts { return parameter && parameter.symbol; } + export function getJSDocHost(node: JSDocTag): HasJSDoc { + Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment); + return node.parent!.parent!; + } + export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined { const name = node.name.escapedText; const { typeParameters } = (node.parent.parent.parent as ts.SignatureDeclaration | ts.InterfaceDeclaration | ts.ClassDeclaration); diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 2116d87dc6f47..5873a5940773a 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -300,6 +300,11 @@ namespace ts { * @property {number} age * @property {string} name */`); + parsesCorrectly("<> characters", +`/** + * @param x hi +< > still part of the previous comment + */`); }); }); describe("getFirstToken", () => { diff --git a/src/services/completions.ts b/src/services/completions.ts index e271ef1210405..44ad611de79aa 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -581,11 +581,10 @@ namespace ts.Completions { return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters }; - type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; + type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression { switch (tag.kind) { - case SyntaxKind.JSDocAugmentsTag: case SyntaxKind.JSDocParameterTag: case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocReturnTag: diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json new file mode 100644 index 0000000000000..93d7cf147373a --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json @@ -0,0 +1,35 @@ +{ + "kind": "JSDocComment", + "pos": 0, + "end": 61, + "tags": { + "0": { + "kind": "JSDocParameterTag", + "pos": 7, + "end": 16, + "atToken": { + "kind": "AtToken", + "pos": 7, + "end": 8 + }, + "tagName": { + "kind": "Identifier", + "pos": 8, + "end": 13, + "escapedText": "param" + }, + "name": { + "kind": "Identifier", + "pos": 14, + "end": 15, + "escapedText": "x" + }, + "isNameFirst": true, + "isBracketed": false, + "comment": "hi\n< > still part of the previous comment" + }, + "length": 1, + "pos": 7, + "end": 16 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt b/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt index b5ac97db31cf0..4c234c3e0259b 100644 --- a/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt +++ b/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt @@ -1,14 +1,20 @@ -/a.js(2,14): error TS1005: '{' expected. +/a.js(2,14): error TS1003: Identifier expected. +/a.js(2,14): error TS8023: JSDoc '@augments ' does not match the 'extends A' clause. +/a.js(5,14): error TS2339: Property 'x' does not exist on type 'B'. -==== /a.js (1 errors) ==== +==== /a.js (3 errors) ==== class A { constructor() { this.x = 0; } } /** @augments */ - ~ -!!! error TS1005: '{' expected. + +!!! error TS1003: Identifier expected. + +!!! error TS8023: JSDoc '@augments ' does not match the 'extends A' clause. class B extends A { m() { this.x + ~ +!!! error TS2339: Property 'x' does not exist on type 'B'. } } \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugmentsMissingType.symbols b/tests/baselines/reference/jsdocAugmentsMissingType.symbols index 0ef9debf11918..e7952c6f97d75 100644 --- a/tests/baselines/reference/jsdocAugmentsMissingType.symbols +++ b/tests/baselines/reference/jsdocAugmentsMissingType.symbols @@ -14,9 +14,7 @@ class B extends A { >m : Symbol(B.m, Decl(a.js, 2, 19)) this.x ->this.x : Symbol(A.x, Decl(a.js, 0, 25)) >this : Symbol(B, Decl(a.js, 0, 41)) ->x : Symbol(A.x, Decl(a.js, 0, 25)) } } diff --git a/tests/baselines/reference/jsdocAugmentsMissingType.types b/tests/baselines/reference/jsdocAugmentsMissingType.types index 6d687d81d161b..de0ba42cdd26a 100644 --- a/tests/baselines/reference/jsdocAugmentsMissingType.types +++ b/tests/baselines/reference/jsdocAugmentsMissingType.types @@ -10,15 +10,15 @@ class A { constructor() { this.x = 0; } } /** @augments */ class B extends A { >B : B ->A : A +>A : typeof A m() { >m : () => void this.x ->this.x : number +>this.x : any >this : this ->x : number +>x : any } } diff --git a/tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt b/tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt new file mode 100644 index 0000000000000..f6d9d8821014c --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt @@ -0,0 +1,12 @@ +/b.js(4,15): error TS8023: JSDoc '@augments A' does not match the 'extends B' clause. + + +==== /b.js (1 errors) ==== + class A {} + class B {} + + /** @augments A */ + ~ +!!! error TS8023: JSDoc '@augments A' does not match the 'extends B' clause. + class C extends B {} + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugments_nameMismatch.symbols b/tests/baselines/reference/jsdocAugments_nameMismatch.symbols new file mode 100644 index 0000000000000..14accc021d115 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_nameMismatch.symbols @@ -0,0 +1,12 @@ +=== /b.js === +class A {} +>A : Symbol(A, Decl(b.js, 0, 0)) + +class B {} +>B : Symbol(B, Decl(b.js, 0, 10)) + +/** @augments A */ +class C extends B {} +>C : Symbol(C, Decl(b.js, 1, 10)) +>B : Symbol(B, Decl(b.js, 0, 10)) + diff --git a/tests/baselines/reference/jsdocAugments_nameMismatch.types b/tests/baselines/reference/jsdocAugments_nameMismatch.types new file mode 100644 index 0000000000000..9522124750b28 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_nameMismatch.types @@ -0,0 +1,12 @@ +=== /b.js === +class A {} +>A : A + +class B {} +>B : B + +/** @augments A */ +class C extends B {} +>C : C +>B : A + diff --git a/tests/baselines/reference/jsdocAugments_noExtends.symbols b/tests/baselines/reference/jsdocAugments_noExtends.symbols new file mode 100644 index 0000000000000..cd3d8575f26c9 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_noExtends.symbols @@ -0,0 +1,21 @@ +=== /b.js === +class A { constructor() { this.x = 0; } } +>A : Symbol(A, Decl(b.js, 0, 0)) +>this.x : Symbol(A.x, Decl(b.js, 0, 25)) +>this : Symbol(A, Decl(b.js, 0, 0)) +>x : Symbol(A.x, Decl(b.js, 0, 25)) + +/** @augments A */ +class B { +>B : Symbol(B, Decl(b.js, 0, 41)) + + m() { +>m : Symbol(B.m, Decl(b.js, 3, 9)) + + return this.x; +>this.x : Symbol(A.x, Decl(b.js, 0, 25)) +>this : Symbol(B, Decl(b.js, 0, 41)) +>x : Symbol(A.x, Decl(b.js, 0, 25)) + } +} + diff --git a/tests/baselines/reference/jsdocAugments_noExtends.types b/tests/baselines/reference/jsdocAugments_noExtends.types new file mode 100644 index 0000000000000..5a2c5632ab700 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_noExtends.types @@ -0,0 +1,23 @@ +=== /b.js === +class A { constructor() { this.x = 0; } } +>A : A +>this.x = 0 : 0 +>this.x : number +>this : this +>x : number +>0 : 0 + +/** @augments A */ +class B { +>B : B + + m() { +>m : () => number + + return this.x; +>this.x : number +>this : this +>x : number + } +} + diff --git a/tests/baselines/reference/jsdocAugments_notAClass.errors.txt b/tests/baselines/reference/jsdocAugments_notAClass.errors.txt new file mode 100644 index 0000000000000..9f8528f0cd804 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_notAClass.errors.txt @@ -0,0 +1,10 @@ +/b.js(3,10): error TS8022: JSDoc '@augments' is not attached to a class declaration. + + +==== /b.js (1 errors) ==== + class A {} + /** @augments A */ + function b() {} + ~ +!!! error TS8022: JSDoc '@augments' is not attached to a class declaration. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugments_notAClass.symbols b/tests/baselines/reference/jsdocAugments_notAClass.symbols new file mode 100644 index 0000000000000..59f8333d71283 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_notAClass.symbols @@ -0,0 +1,8 @@ +=== /b.js === +class A {} +>A : Symbol(A, Decl(b.js, 0, 0)) + +/** @augments A */ +function b() {} +>b : Symbol(b, Decl(b.js, 0, 10)) + diff --git a/tests/baselines/reference/jsdocAugments_notAClass.types b/tests/baselines/reference/jsdocAugments_notAClass.types new file mode 100644 index 0000000000000..e927253029838 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_notAClass.types @@ -0,0 +1,8 @@ +=== /b.js === +class A {} +>A : A + +/** @augments A */ +function b() {} +>b : () => void + diff --git a/tests/baselines/reference/jsdocAugments_withTypeParameter.symbols b/tests/baselines/reference/jsdocAugments_withTypeParameter.symbols new file mode 100644 index 0000000000000..59c0727331995 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_withTypeParameter.symbols @@ -0,0 +1,23 @@ +=== /a.d.ts === +declare class A { x: T } +>A : Symbol(A, Decl(a.d.ts, 0, 0)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) + +=== /b.js === +/** @augments A */ +class B extends A { +>B : Symbol(B, Decl(b.js, 0, 0)) +>A : Symbol(A, Decl(a.d.ts, 0, 0)) + + m() { +>m : Symbol(B.m, Decl(b.js, 1, 19)) + + return this.x; +>this.x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>this : Symbol(B, Decl(b.js, 0, 0)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) + } +} + diff --git a/tests/baselines/reference/jsdocAugments_withTypeParameter.types b/tests/baselines/reference/jsdocAugments_withTypeParameter.types new file mode 100644 index 0000000000000..fb922808b7fc3 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_withTypeParameter.types @@ -0,0 +1,23 @@ +=== /a.d.ts === +declare class A { x: T } +>A : A +>T : T +>x : T +>T : T + +=== /b.js === +/** @augments A */ +class B extends A { +>B : B +>A : A + + m() { +>m : () => number + + return this.x; +>this.x : number +>this : this +>x : number + } +} + diff --git a/tests/cases/compiler/jsdocAugments_nameMismatch.ts b/tests/cases/compiler/jsdocAugments_nameMismatch.ts new file mode 100644 index 0000000000000..8112c57ae830d --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_nameMismatch.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /b.js +class A {} +class B {} + +/** @augments A */ +class C extends B {} diff --git a/tests/cases/compiler/jsdocAugments_noExtends.ts b/tests/cases/compiler/jsdocAugments_noExtends.ts new file mode 100644 index 0000000000000..87719522b9f99 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_noExtends.ts @@ -0,0 +1,13 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /b.js +class A { constructor() { this.x = 0; } } + +/** @augments A */ +class B { + m() { + return this.x; + } +} diff --git a/tests/cases/compiler/jsdocAugments_notAClass.ts b/tests/cases/compiler/jsdocAugments_notAClass.ts new file mode 100644 index 0000000000000..cd25e91ad3d06 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_notAClass.ts @@ -0,0 +1,8 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /b.js +class A {} +/** @augments A */ +function b() {} diff --git a/tests/cases/compiler/jsdocAugments_withTypeParameter.ts b/tests/cases/compiler/jsdocAugments_withTypeParameter.ts new file mode 100644 index 0000000000000..e94df03fbc0a5 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_withTypeParameter.ts @@ -0,0 +1,14 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.d.ts +declare class A { x: T } + +// @Filename: /b.js +/** @augments A */ +class B extends A { + m() { + return this.x; + } +} diff --git a/tests/cases/fourslash/jsDocAugments.ts b/tests/cases/fourslash/jsDocAugments.ts index 24458c529fb92..1938cd0e2aaaa 100644 --- a/tests/cases/fourslash/jsDocAugments.ts +++ b/tests/cases/fourslash/jsDocAugments.ts @@ -15,7 +15,7 @@ // @Filename: declarations.d.ts //// declare class Thing { -//// mine: T; +//// mine: T; //// } goTo.marker();