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
This commit is contained in:
Titian Cernicova-Dragomir 2020-02-27 19:27:37 +02:00 committed by GitHub
parent e3ec3d1942
commit f883bf3acb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 981 additions and 66 deletions

View file

@ -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 = [<ObjectType>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(<ImportTypeNode>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:

View file

@ -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("{");

View file

@ -479,6 +479,9 @@ namespace ts {
visitNode(cbNode, (<JSDocPropertyLikeTag>node).name));
case SyntaxKind.JSDocAuthorTag:
return visitNode(cbNode, (node as JSDocTag).tagName);
case SyntaxKind.JSDocImplementsTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (<JSDocImplementsTag>node).class);
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (<JSDocAugmentsTag>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 = <JSDocImplementsTag>createNode(SyntaxKind.JSDocImplementsTag, start);
result.tagName = tagName;
result.class = parseExpressionWithTypeArgumentsForAugments();
return finishNode(result);
}
function parseAugmentsTag(start: number, tagName: Identifier): JSDocAugmentsTag {
const result = <JSDocAugmentsTag>createNode(SyntaxKind.JSDocAugmentsTag, start);
result.tagName = tagName;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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<T extends JSDocTag>(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;
}

View file

@ -10,7 +10,7 @@ namespace ts.codefix {
getCodeActions(context) {
const { sourceFile, span } = context;
const classDeclaration = getClass(sourceFile, span.start);
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => {
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(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);
}
}

View file

@ -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:

View file

@ -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<T extends JSDocTag>(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;

View file

@ -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<T extends JSDocTag>(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;

View file

@ -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.
}

View file

@ -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 {
}

View file

@ -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))
}

View file

@ -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
}

View file

@ -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.
}

View file

@ -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 {
}

View file

@ -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))
}

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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 {
}

View file

@ -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 {
}

View file

@ -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))
}

View file

@ -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
}

View file

@ -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 }
}

View file

@ -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;
}

View file

@ -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))
}

View file

@ -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
}

View file

@ -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'.
}

View file

@ -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 {
}

View file

@ -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))
}

View file

@ -0,0 +1,11 @@
=== /defs.d.ts ===
interface Sig {
[index: string]: string
>index : string
}
=== /a.js ===
/** @implements {Sig} */
class B {
>B : B
}

View file

@ -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 {
}

View file

@ -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 {
}

View file

@ -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;
}
}

View file

@ -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 {
}

View file

@ -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 }
}

View file

@ -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 {
}