From f883bf3acbb207dfa1dc134738abcf565f14a835 Mon Sep 17 00:00:00 2001 From: Titian Cernicova-Dragomir Date: Thu, 27 Feb 2020 19:27:37 +0200 Subject: [PATCH] Adding support for @implements. (#36292) * Adding support for @implements. * Fixed code review issues for @implements, added some more tests. * Fixed declaration emit for @interface * Improved getImplementsTypes to not cache the results since it is only used once. * Removed unnecessary checks from getImplementsTypes --- src/compiler/checker.ts | 37 ++++++++++- src/compiler/emitter.ts | 5 +- src/compiler/parser.ts | 13 ++++ src/compiler/types.ts | 8 ++- src/compiler/utilities.ts | 13 ++-- src/compiler/utilitiesPublic.ts | 16 ++++- .../fixClassIncorrectlyImplementsInterface.ts | 4 +- src/services/jsDoc.ts | 2 + .../reference/api/tsserverlibrary.d.ts | 66 +++++++++++-------- tests/baselines/reference/api/typescript.d.ts | 66 +++++++++++-------- .../jsdocImplements_class.errors.txt | 35 ++++++++++ .../reference/jsdocImplements_class.js | 40 +++++++++++ .../reference/jsdocImplements_class.symbols | 31 +++++++++ .../reference/jsdocImplements_class.types | 34 ++++++++++ .../jsdocImplements_interface.errors.txt | 36 ++++++++++ .../reference/jsdocImplements_interface.js | 38 +++++++++++ .../jsdocImplements_interface.symbols | 33 ++++++++++ .../reference/jsdocImplements_interface.types | 33 ++++++++++ ...ocImplements_interface_multiple.errors.txt | 37 +++++++++++ .../jsdocImplements_interface_multiple.js | 50 ++++++++++++++ ...jsdocImplements_interface_multiple.symbols | 45 +++++++++++++ .../jsdocImplements_interface_multiple.types | 44 +++++++++++++ .../jsdocImplements_missingType.errors.txt | 11 ++++ .../reference/jsdocImplements_missingType.js | 16 +++++ .../jsdocImplements_missingType.symbols | 12 ++++ .../jsdocImplements_missingType.types | 14 ++++ .../jsdocImplements_properties.errors.txt | 23 +++++++ .../reference/jsdocImplements_properties.js | 33 ++++++++++ .../jsdocImplements_properties.symbols | 29 ++++++++ .../jsdocImplements_properties.types | 34 ++++++++++ .../jsdocImplements_signatures.errors.txt | 16 +++++ .../reference/jsdocImplements_signatures.js | 18 +++++ .../jsdocImplements_signatures.symbols | 13 ++++ .../jsdocImplements_signatures.types | 11 ++++ .../jsdoc/jsdocImplements_class.ts | 25 +++++++ .../jsdoc/jsdocImplements_interface.ts | 26 ++++++++ .../jsdocImplements_interface_multiple.ts | 35 ++++++++++ .../jsdoc/jsdocImplements_missingType.ts | 11 ++++ .../jsdoc/jsdocImplements_properties.ts | 20 ++++++ .../jsdoc/jsdocImplements_signatures.ts | 14 ++++ 40 files changed, 981 insertions(+), 66 deletions(-) create mode 100644 tests/baselines/reference/jsdocImplements_class.errors.txt create mode 100644 tests/baselines/reference/jsdocImplements_class.js create mode 100644 tests/baselines/reference/jsdocImplements_class.symbols create mode 100644 tests/baselines/reference/jsdocImplements_class.types create mode 100644 tests/baselines/reference/jsdocImplements_interface.errors.txt create mode 100644 tests/baselines/reference/jsdocImplements_interface.js create mode 100644 tests/baselines/reference/jsdocImplements_interface.symbols create mode 100644 tests/baselines/reference/jsdocImplements_interface.types create mode 100644 tests/baselines/reference/jsdocImplements_interface_multiple.errors.txt create mode 100644 tests/baselines/reference/jsdocImplements_interface_multiple.js create mode 100644 tests/baselines/reference/jsdocImplements_interface_multiple.symbols create mode 100644 tests/baselines/reference/jsdocImplements_interface_multiple.types create mode 100644 tests/baselines/reference/jsdocImplements_missingType.errors.txt create mode 100644 tests/baselines/reference/jsdocImplements_missingType.js create mode 100644 tests/baselines/reference/jsdocImplements_missingType.symbols create mode 100644 tests/baselines/reference/jsdocImplements_missingType.types create mode 100644 tests/baselines/reference/jsdocImplements_properties.errors.txt create mode 100644 tests/baselines/reference/jsdocImplements_properties.js create mode 100644 tests/baselines/reference/jsdocImplements_properties.symbols create mode 100644 tests/baselines/reference/jsdocImplements_properties.types create mode 100644 tests/baselines/reference/jsdocImplements_signatures.errors.txt create mode 100644 tests/baselines/reference/jsdocImplements_signatures.js create mode 100644 tests/baselines/reference/jsdocImplements_signatures.symbols create mode 100644 tests/baselines/reference/jsdocImplements_signatures.types create mode 100644 tests/cases/conformance/jsdoc/jsdocImplements_class.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocImplements_interface.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocImplements_interface_multiple.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocImplements_missingType.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocImplements_properties.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocImplements_signatures.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3f1474f5d0c..ba5a482a4a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5889,9 +5889,13 @@ namespace ts { const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context)); const classType = getDeclaredTypeOfClassOrInterface(symbol); const baseTypes = getBaseTypes(classType); + const implementsTypes = getImplementsTypes(classType); const staticType = getTypeOfSymbol(symbol); const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType); - const heritageClauses = !length(baseTypes) ? undefined : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))]; + const heritageClauses = [ + ...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))], + ...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))] + ]; const symbolProps = getPropertiesOfType(classType); const publicSymbolProps = filter(symbolProps, s => { const valueDecl = s.valueDeclaration; @@ -8251,6 +8255,26 @@ namespace ts { return type.resolvedBaseConstructorType; } + function getImplementsTypes(type: InterfaceType): BaseType[] { + let resolvedImplementsTypes: BaseType[] = emptyArray; + for (const declaration of type.symbol.declarations) { + const implementsTypeNodes = getEffectiveImplementsTypeNodes(declaration as ClassLikeDeclaration); + if (!implementsTypeNodes) continue; + for (const node of implementsTypeNodes) { + const implementsType = getTypeFromTypeNode(node); + if (implementsType !== errorType) { + if (resolvedImplementsTypes === emptyArray) { + resolvedImplementsTypes = [implementsType]; + } + else { + resolvedImplementsTypes.push(implementsType); + } + } + } + } + return resolvedImplementsTypes; + } + function getBaseTypes(type: InterfaceType): BaseType[] { if (!type.resolvedBaseTypes) { if (type.objectFlags & ObjectFlags.Tuple) { @@ -30339,6 +30363,13 @@ namespace ts { checkSignatureDeclaration(node); } + function checkJSDocImplementsTag(node: JSDocImplementsTag): void { + const classLike = getJSDocHost(node); + if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { + error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName)); + return; + } + } function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { const classLike = getJSDocHost(node); if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { @@ -32609,7 +32640,7 @@ namespace ts { } } - const implementedTypeNodes = getClassImplementsHeritageClauseElements(node); + const implementedTypeNodes = getEffectiveImplementsTypeNodes(node); if (implementedTypeNodes) { for (const typeRefNode of implementedTypeNodes) { if (!isEntityNameExpression(typeRefNode.expression)) { @@ -33829,6 +33860,8 @@ namespace ts { return checkImportType(node); case SyntaxKind.JSDocAugmentsTag: return checkJSDocAugmentsTag(node as JSDocAugmentsTag); + case SyntaxKind.JSDocImplementsTag: + return checkJSDocImplementsTag(node as JSDocImplementsTag); case SyntaxKind.JSDocTypedefTag: case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocEnumTag: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ad7ea6fce32..adbe97212e2 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1519,8 +1519,9 @@ namespace ts { case SyntaxKind.JSDocThisTag: case SyntaxKind.JSDocEnumTag: return emitJSDocSimpleTypedTag(node as JSDocTypeTag); + case SyntaxKind.JSDocImplementsTag: case SyntaxKind.JSDocAugmentsTag: - return emitJSDocAugmentsTag(node as JSDocAugmentsTag); + return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag); case SyntaxKind.JSDocTemplateTag: return emitJSDocTemplateTag(node as JSDocTemplateTag); case SyntaxKind.JSDocTypedefTag: @@ -3468,7 +3469,7 @@ namespace ts { emitJSDocComment(tag.comment); } - function emitJSDocAugmentsTag(tag: JSDocAugmentsTag) { + function emitJSDocHeritageTag(tag: JSDocImplementsTag | JSDocAugmentsTag) { emitJSDocTagName(tag.tagName); writeSpace(); writePunctuation("{"); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5804c2c4253..802756971d2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -479,6 +479,9 @@ namespace ts { visitNode(cbNode, (node).name)); case SyntaxKind.JSDocAuthorTag: return visitNode(cbNode, (node as JSDocTag).tagName); + case SyntaxKind.JSDocImplementsTag: + return visitNode(cbNode, (node as JSDocTag).tagName) || + visitNode(cbNode, (node).class); case SyntaxKind.JSDocAugmentsTag: return visitNode(cbNode, (node as JSDocTag).tagName) || visitNode(cbNode, (node).class); @@ -6999,6 +7002,9 @@ namespace ts { case "author": tag = parseAuthorTag(start, tagName, margin); break; + case "implements": + tag = parseImplementsTag(start, tagName); + break; case "augments": case "extends": tag = parseAugmentsTag(start, tagName); @@ -7355,6 +7361,13 @@ namespace ts { } } + function parseImplementsTag(start: number, tagName: Identifier): JSDocImplementsTag { + const result = createNode(SyntaxKind.JSDocImplementsTag, start); + result.tagName = tagName; + result.class = parseExpressionWithTypeArgumentsForAugments(); + return finishNode(result); + } + function parseAugmentsTag(start: number, tagName: Identifier): JSDocAugmentsTag { const result = createNode(SyntaxKind.JSDocAugmentsTag, start); result.tagName = tagName; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 36c3f44268d..519b9609383 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -471,6 +471,7 @@ namespace ts { JSDocSignature, JSDocTag, JSDocAugmentsTag, + JSDocImplementsTag, JSDocAuthorTag, JSDocClassTag, JSDocPublicTag, @@ -1986,7 +1987,7 @@ namespace ts { export interface ExpressionWithTypeArguments extends NodeWithTypeArguments { kind: SyntaxKind.ExpressionWithTypeArguments; - parent: HeritageClause | JSDocAugmentsTag; + parent: HeritageClause | JSDocAugmentsTag | JSDocImplementsTag; expression: LeftHandSideExpression; } @@ -2660,6 +2661,11 @@ namespace ts { class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; } + export interface JSDocImplementsTag extends JSDocTag { + kind: SyntaxKind.JSDocImplementsTag; + class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; + } + export interface JSDocAuthorTag extends JSDocTag { kind: SyntaxKind.JSDocAuthorTag; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7badd3af99e..7fa1c520643 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2807,15 +2807,20 @@ namespace ts { return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined; } - export function getClassImplementsHeritageClauseElements(node: ClassLikeDeclaration) { - const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword); - return heritageClause ? heritageClause.types : undefined; + export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{ + if(isInJSFile(node)) { + return getJSDocImplementsTags(node).map(n => n.class); + } + else { + const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword); + return heritageClause?.types; + } } /** Returns the node in an `extends` or `implements` clause of a class or interface. */ export function getAllSuperTypeNodes(node: Node): readonly TypeNode[] { return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray : - isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getClassImplementsHeritageClauseElements(node)) || emptyArray : + isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getEffectiveImplementsTypeNodes(node)) || emptyArray : emptyArray; } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 052144fdc00..516b091bde5 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -671,6 +671,11 @@ namespace ts { return getFirstJSDocTag(node, isJSDocAugmentsTag); } + /** Gets the JSDoc implements tags for the node if present */ + export function getJSDocImplementsTags(node: Node): readonly JSDocImplementsTag[] { + return getAllJSDocTags(node, isJSDocImplementsTag); + } + /** Gets the JSDoc class tag for the node if present */ export function getJSDocClassTag(node: Node): JSDocClassTag | undefined { return getFirstJSDocTag(node, isJSDocClassTag); @@ -787,7 +792,12 @@ namespace ts { return find(getJSDocTags(node), predicate); } - /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + /** Gets all JSDoc tags that match a specified predicate */ + export function getAllJSDocTags(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[] { + return getJSDocTags(node).filter(predicate); + } + + /** Gets all JSDoc tags of a specified kind */ export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[] { return getJSDocTags(node).filter(doc => doc.kind === kind); } @@ -1582,6 +1592,10 @@ namespace ts { return node.kind === SyntaxKind.JSDocAugmentsTag; } + export function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag { + return node.kind === SyntaxKind.JSDocImplementsTag; + } + export function isJSDocClassTag(node: Node): node is JSDocClassTag { return node.kind === SyntaxKind.JSDocClassTag; } diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 4ee8ac749de..4059ee03ca7 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -10,7 +10,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile, span } = context; const classDeclaration = getClass(sourceFile, span.start); - return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { + return mapDefined(getEffectiveImplementsTypeNodes(classDeclaration), implementedTypeNode => { const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(context, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences)); return changes.length === 0 ? undefined : createCodeFixAction(fixId, changes, [Diagnostics.Implement_interface_0, implementedTypeNode.getText(sourceFile)], fixId, Diagnostics.Implement_all_unimplemented_interfaces); }); @@ -21,7 +21,7 @@ namespace ts.codefix { return codeFixAll(context, errorCodes, (changes, diag) => { const classDeclaration = getClass(diag.file, diag.start); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { - for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)!) { + for (const implementedTypeNode of getEffectiveImplementsTypeNodes(classDeclaration)!) { addMissingDeclarations(context, implementedTypeNode, diag.file, classDeclaration, changes, context.preferences); } } diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 995561c016b..7118fe8b761 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -129,6 +129,8 @@ namespace ts.JsDoc { function getCommentText(tag: JSDocTag): string | undefined { const { comment } = tag; switch (tag.kind) { + case SyntaxKind.JSDocImplementsTag: + return withNode((tag as JSDocImplementsTag).class); case SyntaxKind.JSDocAugmentsTag: return withNode((tag as JSDocAugmentsTag).class); case SyntaxKind.JSDocTemplateTag: diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 03a59f53345..0cb0c0fa0dc 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -383,29 +383,30 @@ declare namespace ts { JSDocSignature = 305, JSDocTag = 306, JSDocAugmentsTag = 307, - JSDocAuthorTag = 308, - JSDocClassTag = 309, - JSDocPublicTag = 310, - JSDocPrivateTag = 311, - JSDocProtectedTag = 312, - JSDocReadonlyTag = 313, - JSDocCallbackTag = 314, - JSDocEnumTag = 315, - JSDocParameterTag = 316, - JSDocReturnTag = 317, - JSDocThisTag = 318, - JSDocTypeTag = 319, - JSDocTemplateTag = 320, - JSDocTypedefTag = 321, - JSDocPropertyTag = 322, - SyntaxList = 323, - NotEmittedStatement = 324, - PartiallyEmittedExpression = 325, - CommaListExpression = 326, - MergeDeclarationMarker = 327, - EndOfDeclarationMarker = 328, - SyntheticReferenceExpression = 329, - Count = 330, + JSDocImplementsTag = 308, + JSDocAuthorTag = 309, + JSDocClassTag = 310, + JSDocPublicTag = 311, + JSDocPrivateTag = 312, + JSDocProtectedTag = 313, + JSDocReadonlyTag = 314, + JSDocCallbackTag = 315, + JSDocEnumTag = 316, + JSDocParameterTag = 317, + JSDocReturnTag = 318, + JSDocThisTag = 319, + JSDocTypeTag = 320, + JSDocTemplateTag = 321, + JSDocTypedefTag = 322, + JSDocPropertyTag = 323, + SyntaxList = 324, + NotEmittedStatement = 325, + PartiallyEmittedExpression = 326, + CommaListExpression = 327, + MergeDeclarationMarker = 328, + EndOfDeclarationMarker = 329, + SyntheticReferenceExpression = 330, + Count = 331, FirstAssignment = 62, LastAssignment = 74, FirstCompoundAssignment = 63, @@ -434,9 +435,9 @@ declare namespace ts { LastStatement = 241, FirstNode = 153, FirstJSDocNode = 294, - LastJSDocNode = 322, + LastJSDocNode = 323, FirstJSDocTagNode = 306, - LastJSDocTagNode = 322, + LastJSDocTagNode = 323, } export enum NodeFlags { None = 0, @@ -1145,7 +1146,7 @@ declare namespace ts { } export interface ExpressionWithTypeArguments extends NodeWithTypeArguments { kind: SyntaxKind.ExpressionWithTypeArguments; - parent: HeritageClause | JSDocAugmentsTag; + parent: HeritageClause | JSDocAugmentsTag | JSDocImplementsTag; expression: LeftHandSideExpression; } export interface NewExpression extends PrimaryExpression, Declaration { @@ -1635,6 +1636,12 @@ declare namespace ts { expression: Identifier | PropertyAccessEntityNameExpression; }; } + export interface JSDocImplementsTag extends JSDocTag { + kind: SyntaxKind.JSDocImplementsTag; + class: ExpressionWithTypeArguments & { + expression: Identifier | PropertyAccessEntityNameExpression; + }; + } export interface JSDocAuthorTag extends JSDocTag { kind: SyntaxKind.JSDocAuthorTag; } @@ -3516,6 +3523,8 @@ declare namespace ts { function hasJSDocParameterTags(node: FunctionLikeDeclaration | SignatureDeclaration): boolean; /** Gets the JSDoc augments tag for the node if present */ function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined; + /** Gets the JSDoc implements tags for the node if present */ + function getJSDocImplementsTags(node: Node): readonly JSDocImplementsTag[]; /** Gets the JSDoc class tag for the node if present */ function getJSDocClassTag(node: Node): JSDocClassTag | undefined; /** Gets the JSDoc public tag for the node if present */ @@ -3557,7 +3566,9 @@ declare namespace ts { function getJSDocReturnType(node: Node): TypeNode | undefined; /** Get all JSDoc tags related to a node, including those on parent nodes. */ function getJSDocTags(node: Node): readonly JSDocTag[]; - /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + /** Gets all JSDoc tags that match a specified predicate */ + function getAllJSDocTags(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[]; + /** Gets all JSDoc tags of a specified kind */ function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[]; /** * Gets the effective type parameters. If the node was parsed in a @@ -3733,6 +3744,7 @@ declare namespace ts { function isJSDoc(node: Node): node is JSDoc; function isJSDocAuthorTag(node: Node): node is JSDocAuthorTag; function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag; + function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag; function isJSDocClassTag(node: Node): node is JSDocClassTag; function isJSDocPublicTag(node: Node): node is JSDocPublicTag; function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 469e9e5b6aa..91eafc2894e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -383,29 +383,30 @@ declare namespace ts { JSDocSignature = 305, JSDocTag = 306, JSDocAugmentsTag = 307, - JSDocAuthorTag = 308, - JSDocClassTag = 309, - JSDocPublicTag = 310, - JSDocPrivateTag = 311, - JSDocProtectedTag = 312, - JSDocReadonlyTag = 313, - JSDocCallbackTag = 314, - JSDocEnumTag = 315, - JSDocParameterTag = 316, - JSDocReturnTag = 317, - JSDocThisTag = 318, - JSDocTypeTag = 319, - JSDocTemplateTag = 320, - JSDocTypedefTag = 321, - JSDocPropertyTag = 322, - SyntaxList = 323, - NotEmittedStatement = 324, - PartiallyEmittedExpression = 325, - CommaListExpression = 326, - MergeDeclarationMarker = 327, - EndOfDeclarationMarker = 328, - SyntheticReferenceExpression = 329, - Count = 330, + JSDocImplementsTag = 308, + JSDocAuthorTag = 309, + JSDocClassTag = 310, + JSDocPublicTag = 311, + JSDocPrivateTag = 312, + JSDocProtectedTag = 313, + JSDocReadonlyTag = 314, + JSDocCallbackTag = 315, + JSDocEnumTag = 316, + JSDocParameterTag = 317, + JSDocReturnTag = 318, + JSDocThisTag = 319, + JSDocTypeTag = 320, + JSDocTemplateTag = 321, + JSDocTypedefTag = 322, + JSDocPropertyTag = 323, + SyntaxList = 324, + NotEmittedStatement = 325, + PartiallyEmittedExpression = 326, + CommaListExpression = 327, + MergeDeclarationMarker = 328, + EndOfDeclarationMarker = 329, + SyntheticReferenceExpression = 330, + Count = 331, FirstAssignment = 62, LastAssignment = 74, FirstCompoundAssignment = 63, @@ -434,9 +435,9 @@ declare namespace ts { LastStatement = 241, FirstNode = 153, FirstJSDocNode = 294, - LastJSDocNode = 322, + LastJSDocNode = 323, FirstJSDocTagNode = 306, - LastJSDocTagNode = 322, + LastJSDocTagNode = 323, } export enum NodeFlags { None = 0, @@ -1145,7 +1146,7 @@ declare namespace ts { } export interface ExpressionWithTypeArguments extends NodeWithTypeArguments { kind: SyntaxKind.ExpressionWithTypeArguments; - parent: HeritageClause | JSDocAugmentsTag; + parent: HeritageClause | JSDocAugmentsTag | JSDocImplementsTag; expression: LeftHandSideExpression; } export interface NewExpression extends PrimaryExpression, Declaration { @@ -1635,6 +1636,12 @@ declare namespace ts { expression: Identifier | PropertyAccessEntityNameExpression; }; } + export interface JSDocImplementsTag extends JSDocTag { + kind: SyntaxKind.JSDocImplementsTag; + class: ExpressionWithTypeArguments & { + expression: Identifier | PropertyAccessEntityNameExpression; + }; + } export interface JSDocAuthorTag extends JSDocTag { kind: SyntaxKind.JSDocAuthorTag; } @@ -3516,6 +3523,8 @@ declare namespace ts { function hasJSDocParameterTags(node: FunctionLikeDeclaration | SignatureDeclaration): boolean; /** Gets the JSDoc augments tag for the node if present */ function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined; + /** Gets the JSDoc implements tags for the node if present */ + function getJSDocImplementsTags(node: Node): readonly JSDocImplementsTag[]; /** Gets the JSDoc class tag for the node if present */ function getJSDocClassTag(node: Node): JSDocClassTag | undefined; /** Gets the JSDoc public tag for the node if present */ @@ -3557,7 +3566,9 @@ declare namespace ts { function getJSDocReturnType(node: Node): TypeNode | undefined; /** Get all JSDoc tags related to a node, including those on parent nodes. */ function getJSDocTags(node: Node): readonly JSDocTag[]; - /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + /** Gets all JSDoc tags that match a specified predicate */ + function getAllJSDocTags(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[]; + /** Gets all JSDoc tags of a specified kind */ function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[]; /** * Gets the effective type parameters. If the node was parsed in a @@ -3733,6 +3744,7 @@ declare namespace ts { function isJSDoc(node: Node): node is JSDoc; function isJSDocAuthorTag(node: Node): node is JSDocAuthorTag; function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag; + function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag; function isJSDocClassTag(node: Node): node is JSDocClassTag; function isJSDocPublicTag(node: Node): node is JSDocPublicTag; function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag; diff --git a/tests/baselines/reference/jsdocImplements_class.errors.txt b/tests/baselines/reference/jsdocImplements_class.errors.txt new file mode 100644 index 00000000000..b69bf199758 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_class.errors.txt @@ -0,0 +1,35 @@ +/a.js(13,5): error TS2416: Property 'method' in type 'B2' is not assignable to the same property in base type 'A'. + Type '() => string' is not assignable to type '() => number'. + Type 'string' is not assignable to type 'number'. +/a.js(17,7): error TS2720: Class 'B3' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass? + Property 'method' is missing in type 'B3' but required in type 'A'. + + +==== /a.js (2 errors) ==== + class A { + /** @return {number} */ + method() { throw new Error(); } + } + /** @implements {A} */ + class B { + method() { return 0 } + } + + /** @implements A */ + class B2 { + /** @return {string} */ + method() { return "" } + ~~~~~~ +!!! error TS2416: Property 'method' in type 'B2' is not assignable to the same property in base type 'A'. +!!! error TS2416: Type '() => string' is not assignable to type '() => number'. +!!! error TS2416: Type 'string' is not assignable to type 'number'. + } + + /** @implements {A} */ + class B3 { + ~~ +!!! error TS2720: Class 'B3' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass? +!!! error TS2720: Property 'method' is missing in type 'B3' but required in type 'A'. +!!! related TS2728 /a.js:3:5: 'method' is declared here. + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_class.js b/tests/baselines/reference/jsdocImplements_class.js new file mode 100644 index 00000000000..9f24bbb7203 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_class.js @@ -0,0 +1,40 @@ +//// [a.js] +class A { + /** @return {number} */ + method() { throw new Error(); } +} +/** @implements {A} */ +class B { + method() { return 0 } +} + +/** @implements A */ +class B2 { + /** @return {string} */ + method() { return "" } +} + +/** @implements {A} */ +class B3 { +} + + + + +//// [a.d.ts] +declare class A { + /** @return {number} */ + method(): number; +} +/** @implements {A} */ +declare class B implements A { + method(): number; +} +/** @implements A */ +declare class B2 implements A { + /** @return {string} */ + method(): string; +} +/** @implements {A} */ +declare class B3 implements A { +} diff --git a/tests/baselines/reference/jsdocImplements_class.symbols b/tests/baselines/reference/jsdocImplements_class.symbols new file mode 100644 index 00000000000..f7c45ed2567 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_class.symbols @@ -0,0 +1,31 @@ +=== /a.js === +class A { +>A : Symbol(A, Decl(a.js, 0, 0)) + + /** @return {number} */ + method() { throw new Error(); } +>method : Symbol(A.method, Decl(a.js, 0, 9)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} +/** @implements {A} */ +class B { +>B : Symbol(B, Decl(a.js, 3, 1)) + + method() { return 0 } +>method : Symbol(B.method, Decl(a.js, 5, 10)) +} + +/** @implements A */ +class B2 { +>B2 : Symbol(B2, Decl(a.js, 7, 1)) + + /** @return {string} */ + method() { return "" } +>method : Symbol(B2.method, Decl(a.js, 10, 11)) +} + +/** @implements {A} */ +class B3 { +>B3 : Symbol(B3, Decl(a.js, 13, 1)) +} + diff --git a/tests/baselines/reference/jsdocImplements_class.types b/tests/baselines/reference/jsdocImplements_class.types new file mode 100644 index 00000000000..fda48e992b6 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_class.types @@ -0,0 +1,34 @@ +=== /a.js === +class A { +>A : A + + /** @return {number} */ + method() { throw new Error(); } +>method : () => number +>new Error() : Error +>Error : ErrorConstructor +} +/** @implements {A} */ +class B { +>B : B + + method() { return 0 } +>method : () => number +>0 : 0 +} + +/** @implements A */ +class B2 { +>B2 : B2 + + /** @return {string} */ + method() { return "" } +>method : () => string +>"" : "" +} + +/** @implements {A} */ +class B3 { +>B3 : B3 +} + diff --git a/tests/baselines/reference/jsdocImplements_interface.errors.txt b/tests/baselines/reference/jsdocImplements_interface.errors.txt new file mode 100644 index 00000000000..ed554663607 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface.errors.txt @@ -0,0 +1,36 @@ +/a.js(9,5): error TS2416: Property 'mNumber' in type 'B2' is not assignable to the same property in base type 'A'. + Type '() => string' is not assignable to type '() => number'. + Type 'string' is not assignable to type 'number'. +/a.js(14,7): error TS2420: Class 'B3' incorrectly implements interface 'A'. + Property 'mNumber' is missing in type 'B3' but required in type 'A'. + + +==== /defs.d.ts (0 errors) ==== + interface A { + mNumber(): number; + } +==== /a.js (2 errors) ==== + /** @implements A */ + class B { + mNumber() { + return 0; + } + } + /** @implements {A} */ + class B2 { + mNumber() { + ~~~~~~~ +!!! error TS2416: Property 'mNumber' in type 'B2' is not assignable to the same property in base type 'A'. +!!! error TS2416: Type '() => string' is not assignable to type '() => number'. +!!! error TS2416: Type 'string' is not assignable to type 'number'. + return ""; + } + } + /** @implements A */ + class B3 { + ~~ +!!! error TS2420: Class 'B3' incorrectly implements interface 'A'. +!!! error TS2420: Property 'mNumber' is missing in type 'B3' but required in type 'A'. +!!! related TS2728 /defs.d.ts:2:5: 'mNumber' is declared here. + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_interface.js b/tests/baselines/reference/jsdocImplements_interface.js new file mode 100644 index 00000000000..0588f3a2a98 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface.js @@ -0,0 +1,38 @@ +//// [tests/cases/conformance/jsdoc/jsdocImplements_interface.ts] //// + +//// [defs.d.ts] +interface A { + mNumber(): number; +} +//// [a.js] +/** @implements A */ +class B { + mNumber() { + return 0; + } +} +/** @implements {A} */ +class B2 { + mNumber() { + return ""; + } +} +/** @implements A */ +class B3 { +} + + + + +//// [a.d.ts] +/** @implements A */ +declare class B implements A { + mNumber(): number; +} +/** @implements {A} */ +declare class B2 implements A { + mNumber(): string; +} +/** @implements A */ +declare class B3 implements A { +} diff --git a/tests/baselines/reference/jsdocImplements_interface.symbols b/tests/baselines/reference/jsdocImplements_interface.symbols new file mode 100644 index 00000000000..5117208a159 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface.symbols @@ -0,0 +1,33 @@ +=== /defs.d.ts === +interface A { +>A : Symbol(A, Decl(defs.d.ts, 0, 0)) + + mNumber(): number; +>mNumber : Symbol(A.mNumber, Decl(defs.d.ts, 0, 13)) +} +=== /a.js === +/** @implements A */ +class B { +>B : Symbol(B, Decl(a.js, 0, 0)) + + mNumber() { +>mNumber : Symbol(B.mNumber, Decl(a.js, 1, 9)) + + return 0; + } +} +/** @implements {A} */ +class B2 { +>B2 : Symbol(B2, Decl(a.js, 5, 1)) + + mNumber() { +>mNumber : Symbol(B2.mNumber, Decl(a.js, 7, 10)) + + return ""; + } +} +/** @implements A */ +class B3 { +>B3 : Symbol(B3, Decl(a.js, 11, 1)) +} + diff --git a/tests/baselines/reference/jsdocImplements_interface.types b/tests/baselines/reference/jsdocImplements_interface.types new file mode 100644 index 00000000000..b0a053c72b9 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface.types @@ -0,0 +1,33 @@ +=== /defs.d.ts === +interface A { + mNumber(): number; +>mNumber : () => number +} +=== /a.js === +/** @implements A */ +class B { +>B : B + + mNumber() { +>mNumber : () => number + + return 0; +>0 : 0 + } +} +/** @implements {A} */ +class B2 { +>B2 : B2 + + mNumber() { +>mNumber : () => string + + return ""; +>"" : "" + } +} +/** @implements A */ +class B3 { +>B3 : B3 +} + diff --git a/tests/baselines/reference/jsdocImplements_interface_multiple.errors.txt b/tests/baselines/reference/jsdocImplements_interface_multiple.errors.txt new file mode 100644 index 00000000000..334422ee60a --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface_multiple.errors.txt @@ -0,0 +1,37 @@ +/a.js(17,7): error TS2420: Class 'BadSquare' incorrectly implements interface 'Drawable'. + Property 'draw' is missing in type 'BadSquare' but required in type 'Drawable'. + + +==== /defs.d.ts (0 errors) ==== + interface Drawable { + draw(): number; + } + interface Sizable { + size(): number; + } +==== /a.js (1 errors) ==== + /** + * @implements {Drawable} + * @implements Sizable + **/ + class Square { + draw() { + return 0; + } + size() { + return 0; + } + } + /** + * @implements Drawable + * @implements {Sizable} + **/ + class BadSquare { + ~~~~~~~~~ +!!! error TS2420: Class 'BadSquare' incorrectly implements interface 'Drawable'. +!!! error TS2420: Property 'draw' is missing in type 'BadSquare' but required in type 'Drawable'. +!!! related TS2728 /defs.d.ts:2:5: 'draw' is declared here. + size() { + return 0; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_interface_multiple.js b/tests/baselines/reference/jsdocImplements_interface_multiple.js new file mode 100644 index 00000000000..4ea9497f2b9 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface_multiple.js @@ -0,0 +1,50 @@ +//// [tests/cases/conformance/jsdoc/jsdocImplements_interface_multiple.ts] //// + +//// [defs.d.ts] +interface Drawable { + draw(): number; +} +interface Sizable { + size(): number; +} +//// [a.js] +/** + * @implements {Drawable} + * @implements Sizable + **/ +class Square { + draw() { + return 0; + } + size() { + return 0; + } +} +/** + * @implements Drawable + * @implements {Sizable} + **/ +class BadSquare { + size() { + return 0; + } +} + + + +//// [a.d.ts] +/** + * @implements {Drawable} + * @implements Sizable + **/ +declare class Square implements Drawable, Sizable { + draw(): number; + size(): number; +} +/** + * @implements Drawable + * @implements {Sizable} + **/ +declare class BadSquare implements Drawable, Sizable { + size(): number; +} diff --git a/tests/baselines/reference/jsdocImplements_interface_multiple.symbols b/tests/baselines/reference/jsdocImplements_interface_multiple.symbols new file mode 100644 index 00000000000..1b881aeb0b3 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface_multiple.symbols @@ -0,0 +1,45 @@ +=== /defs.d.ts === +interface Drawable { +>Drawable : Symbol(Drawable, Decl(defs.d.ts, 0, 0)) + + draw(): number; +>draw : Symbol(Drawable.draw, Decl(defs.d.ts, 0, 20)) +} +interface Sizable { +>Sizable : Symbol(Sizable, Decl(defs.d.ts, 2, 1)) + + size(): number; +>size : Symbol(Sizable.size, Decl(defs.d.ts, 3, 19)) +} +=== /a.js === +/** + * @implements {Drawable} + * @implements Sizable + **/ +class Square { +>Square : Symbol(Square, Decl(a.js, 0, 0)) + + draw() { +>draw : Symbol(Square.draw, Decl(a.js, 4, 14)) + + return 0; + } + size() { +>size : Symbol(Square.size, Decl(a.js, 7, 5)) + + return 0; + } +} +/** + * @implements Drawable + * @implements {Sizable} + **/ +class BadSquare { +>BadSquare : Symbol(BadSquare, Decl(a.js, 11, 1)) + + size() { +>size : Symbol(BadSquare.size, Decl(a.js, 16, 17)) + + return 0; + } +} diff --git a/tests/baselines/reference/jsdocImplements_interface_multiple.types b/tests/baselines/reference/jsdocImplements_interface_multiple.types new file mode 100644 index 00000000000..2e0077e88a9 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_interface_multiple.types @@ -0,0 +1,44 @@ +=== /defs.d.ts === +interface Drawable { + draw(): number; +>draw : () => number +} +interface Sizable { + size(): number; +>size : () => number +} +=== /a.js === +/** + * @implements {Drawable} + * @implements Sizable + **/ +class Square { +>Square : Square + + draw() { +>draw : () => number + + return 0; +>0 : 0 + } + size() { +>size : () => number + + return 0; +>0 : 0 + } +} +/** + * @implements Drawable + * @implements {Sizable} + **/ +class BadSquare { +>BadSquare : BadSquare + + size() { +>size : () => number + + return 0; +>0 : 0 + } +} diff --git a/tests/baselines/reference/jsdocImplements_missingType.errors.txt b/tests/baselines/reference/jsdocImplements_missingType.errors.txt new file mode 100644 index 00000000000..2e3a04e21b6 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_missingType.errors.txt @@ -0,0 +1,11 @@ +/a.js(2,16): error TS1003: Identifier expected. + + +==== /a.js (1 errors) ==== + class A { constructor() { this.x = 0; } } + /** @implements */ + +!!! error TS1003: Identifier expected. + class B { + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_missingType.js b/tests/baselines/reference/jsdocImplements_missingType.js new file mode 100644 index 00000000000..4042f40ca54 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_missingType.js @@ -0,0 +1,16 @@ +//// [a.js] +class A { constructor() { this.x = 0; } } +/** @implements */ +class B { +} + + + + +//// [a.d.ts] +declare class A { + x: number; +} +/** @implements */ +declare class B { +} diff --git a/tests/baselines/reference/jsdocImplements_missingType.symbols b/tests/baselines/reference/jsdocImplements_missingType.symbols new file mode 100644 index 00000000000..115ac771b92 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_missingType.symbols @@ -0,0 +1,12 @@ +=== /a.js === +class A { constructor() { this.x = 0; } } +>A : Symbol(A, Decl(a.js, 0, 0)) +>this.x : Symbol(A.x, Decl(a.js, 0, 25)) +>this : Symbol(A, Decl(a.js, 0, 0)) +>x : Symbol(A.x, Decl(a.js, 0, 25)) + +/** @implements */ +class B { +>B : Symbol(B, Decl(a.js, 0, 41)) +} + diff --git a/tests/baselines/reference/jsdocImplements_missingType.types b/tests/baselines/reference/jsdocImplements_missingType.types new file mode 100644 index 00000000000..7a63d6aa5f2 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_missingType.types @@ -0,0 +1,14 @@ +=== /a.js === +class A { constructor() { this.x = 0; } } +>A : A +>this.x = 0 : 0 +>this.x : number +>this : this +>x : number +>0 : 0 + +/** @implements */ +class B { +>B : B +} + diff --git a/tests/baselines/reference/jsdocImplements_properties.errors.txt b/tests/baselines/reference/jsdocImplements_properties.errors.txt new file mode 100644 index 00000000000..5d509a500f9 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_properties.errors.txt @@ -0,0 +1,23 @@ +/a.js(3,7): error TS2720: Class 'B' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass? + Property 'x' is missing in type 'B' but required in type 'A'. + + +==== /a.js (1 errors) ==== + class A { constructor() { this.x = 0; } } + /** @implements A*/ + class B {} + ~ +!!! error TS2720: Class 'B' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass? +!!! error TS2720: Property 'x' is missing in type 'B' but required in type 'A'. +!!! related TS2728 /a.js:1:27: 'x' is declared here. + + /** @implements A*/ + class B2 { + x = 10 + } + + /** @implements {A}*/ + class B3 { + constructor() { this.x = 10 } + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_properties.js b/tests/baselines/reference/jsdocImplements_properties.js new file mode 100644 index 00000000000..7030c701b6e --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_properties.js @@ -0,0 +1,33 @@ +//// [a.js] +class A { constructor() { this.x = 0; } } +/** @implements A*/ +class B {} + +/** @implements A*/ +class B2 { + x = 10 +} + +/** @implements {A}*/ +class B3 { + constructor() { this.x = 10 } +} + + + + +//// [a.d.ts] +declare class A { + x: number; +} +/** @implements A*/ +declare class B implements A { +} +/** @implements A*/ +declare class B2 implements A { + x: number; +} +/** @implements {A}*/ +declare class B3 implements A { + x: number; +} diff --git a/tests/baselines/reference/jsdocImplements_properties.symbols b/tests/baselines/reference/jsdocImplements_properties.symbols new file mode 100644 index 00000000000..3f64476a191 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_properties.symbols @@ -0,0 +1,29 @@ +=== /a.js === +class A { constructor() { this.x = 0; } } +>A : Symbol(A, Decl(a.js, 0, 0)) +>this.x : Symbol(A.x, Decl(a.js, 0, 25)) +>this : Symbol(A, Decl(a.js, 0, 0)) +>x : Symbol(A.x, Decl(a.js, 0, 25)) + +/** @implements A*/ +class B {} +>B : Symbol(B, Decl(a.js, 0, 41)) + +/** @implements A*/ +class B2 { +>B2 : Symbol(B2, Decl(a.js, 2, 10)) + + x = 10 +>x : Symbol(B2.x, Decl(a.js, 5, 10)) +} + +/** @implements {A}*/ +class B3 { +>B3 : Symbol(B3, Decl(a.js, 7, 1)) + + constructor() { this.x = 10 } +>this.x : Symbol(B3.x, Decl(a.js, 11, 19)) +>this : Symbol(B3, Decl(a.js, 7, 1)) +>x : Symbol(B3.x, Decl(a.js, 11, 19)) +} + diff --git a/tests/baselines/reference/jsdocImplements_properties.types b/tests/baselines/reference/jsdocImplements_properties.types new file mode 100644 index 00000000000..fb00977b860 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_properties.types @@ -0,0 +1,34 @@ +=== /a.js === +class A { constructor() { this.x = 0; } } +>A : A +>this.x = 0 : 0 +>this.x : number +>this : this +>x : number +>0 : 0 + +/** @implements A*/ +class B {} +>B : B + +/** @implements A*/ +class B2 { +>B2 : B2 + + x = 10 +>x : number +>10 : 10 +} + +/** @implements {A}*/ +class B3 { +>B3 : B3 + + constructor() { this.x = 10 } +>this.x = 10 : 10 +>this.x : number +>this : this +>x : number +>10 : 10 +} + diff --git a/tests/baselines/reference/jsdocImplements_signatures.errors.txt b/tests/baselines/reference/jsdocImplements_signatures.errors.txt new file mode 100644 index 00000000000..05760b4aadc --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_signatures.errors.txt @@ -0,0 +1,16 @@ +/a.js(2,7): error TS2420: Class 'B' incorrectly implements interface 'Sig'. + Index signature is missing in type 'B'. + + +==== /defs.d.ts (0 errors) ==== + interface Sig { + [index: string]: string + } +==== /a.js (1 errors) ==== + /** @implements {Sig} */ + class B { + ~ +!!! error TS2420: Class 'B' incorrectly implements interface 'Sig'. +!!! error TS2420: Index signature is missing in type 'B'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_signatures.js b/tests/baselines/reference/jsdocImplements_signatures.js new file mode 100644 index 00000000000..500b32d0338 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_signatures.js @@ -0,0 +1,18 @@ +//// [tests/cases/conformance/jsdoc/jsdocImplements_signatures.ts] //// + +//// [defs.d.ts] +interface Sig { + [index: string]: string +} +//// [a.js] +/** @implements {Sig} */ +class B { +} + + + + +//// [a.d.ts] +/** @implements {Sig} */ +declare class B implements Sig { +} diff --git a/tests/baselines/reference/jsdocImplements_signatures.symbols b/tests/baselines/reference/jsdocImplements_signatures.symbols new file mode 100644 index 00000000000..32ace896df7 --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_signatures.symbols @@ -0,0 +1,13 @@ +=== /defs.d.ts === +interface Sig { +>Sig : Symbol(Sig, Decl(defs.d.ts, 0, 0)) + + [index: string]: string +>index : Symbol(index, Decl(defs.d.ts, 1, 5)) +} +=== /a.js === +/** @implements {Sig} */ +class B { +>B : Symbol(B, Decl(a.js, 0, 0)) +} + diff --git a/tests/baselines/reference/jsdocImplements_signatures.types b/tests/baselines/reference/jsdocImplements_signatures.types new file mode 100644 index 00000000000..378459402ac --- /dev/null +++ b/tests/baselines/reference/jsdocImplements_signatures.types @@ -0,0 +1,11 @@ +=== /defs.d.ts === +interface Sig { + [index: string]: string +>index : string +} +=== /a.js === +/** @implements {Sig} */ +class B { +>B : B +} + diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_class.ts b/tests/cases/conformance/jsdoc/jsdocImplements_class.ts new file mode 100644 index 00000000000..2a9d63a072a --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocImplements_class.ts @@ -0,0 +1,25 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: ./out + +// @Filename: /a.js +class A { + /** @return {number} */ + method() { throw new Error(); } +} +/** @implements {A} */ +class B { + method() { return 0 } +} + +/** @implements A */ +class B2 { + /** @return {string} */ + method() { return "" } +} + +/** @implements {A} */ +class B3 { +} diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_interface.ts b/tests/cases/conformance/jsdoc/jsdocImplements_interface.ts new file mode 100644 index 00000000000..2c797e81453 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocImplements_interface.ts @@ -0,0 +1,26 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: ./out + +// @Filename: /defs.d.ts +interface A { + mNumber(): number; +} +// @Filename: /a.js +/** @implements A */ +class B { + mNumber() { + return 0; + } +} +/** @implements {A} */ +class B2 { + mNumber() { + return ""; + } +} +/** @implements A */ +class B3 { +} diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_interface_multiple.ts b/tests/cases/conformance/jsdoc/jsdocImplements_interface_multiple.ts new file mode 100644 index 00000000000..187d8c2189a --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocImplements_interface_multiple.ts @@ -0,0 +1,35 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: ./out + +// @Filename: /defs.d.ts +interface Drawable { + draw(): number; +} +interface Sizable { + size(): number; +} +// @Filename: /a.js +/** + * @implements {Drawable} + * @implements Sizable + **/ +class Square { + draw() { + return 0; + } + size() { + return 0; + } +} +/** + * @implements Drawable + * @implements {Sizable} + **/ +class BadSquare { + size() { + return 0; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_missingType.ts b/tests/cases/conformance/jsdoc/jsdocImplements_missingType.ts new file mode 100644 index 00000000000..80604ff1cba --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocImplements_missingType.ts @@ -0,0 +1,11 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: ./out + +// @Filename: /a.js +class A { constructor() { this.x = 0; } } +/** @implements */ +class B { +} diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_properties.ts b/tests/cases/conformance/jsdoc/jsdocImplements_properties.ts new file mode 100644 index 00000000000..8e94fef2925 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocImplements_properties.ts @@ -0,0 +1,20 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: ./out + +// @Filename: /a.js +class A { constructor() { this.x = 0; } } +/** @implements A*/ +class B {} + +/** @implements A*/ +class B2 { + x = 10 +} + +/** @implements {A}*/ +class B3 { + constructor() { this.x = 10 } +} diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_signatures.ts b/tests/cases/conformance/jsdoc/jsdocImplements_signatures.ts new file mode 100644 index 00000000000..a6cc1d972e5 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocImplements_signatures.ts @@ -0,0 +1,14 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: ./out + +// @Filename: /defs.d.ts +interface Sig { + [index: string]: string +} +// @Filename: /a.js +/** @implements {Sig} */ +class B { +}