diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e53600f2ace..ac8a47a9913 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -853,7 +853,8 @@ namespace ts { if (!result) { if (nameNotFoundMessage) { - if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg)) { + if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && + !checkAndReportErrorForExtendingInterface(errorLocation)) { error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg)); } } @@ -937,6 +938,31 @@ namespace ts { return false; } + + function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { + let parentClassExpression = errorLocation; + while (parentClassExpression) { + const kind = parentClassExpression.kind; + if (kind === SyntaxKind.Identifier || kind === SyntaxKind.PropertyAccessExpression) { + parentClassExpression = parentClassExpression.parent; + continue; + } + if (kind === SyntaxKind.ExpressionWithTypeArguments) { + break; + } + return false; + } + if (!parentClassExpression) { + return false; + } + const expression = (parentClassExpression).expression; + if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) { + error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); + return true; + } + return false; + } + function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0); // Block-scoped variables cannot be used before their definition @@ -10137,7 +10163,7 @@ namespace ts { } const prop = getPropertyOfType(apparentType, right.text); if (!prop) { - if (right.text) { + if (right.text && !checkAndReportErrorForExtendingInterface(node)) { error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type.flags & TypeFlags.ThisType ? apparentType : type)); } return unknownType; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 46f1db97625..0e7f02e25fa 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1935,6 +1935,10 @@ "category": "Error", "code": 2688 }, + "Cannot extend an interface '{0}'. Did you mean 'implements'?": { + "category": "Error", + "code": 2689 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/tests/baselines/reference/classExtendingClassLikeType.errors.txt b/tests/baselines/reference/classExtendingClassLikeType.errors.txt index 6f9aef927ea..5edefd78ec5 100644 --- a/tests/baselines/reference/classExtendingClassLikeType.errors.txt +++ b/tests/baselines/reference/classExtendingClassLikeType.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(7,18): error TS2304: Cannot find name 'Base'. +tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(7,18): error TS2689: Cannot extend an interface 'Base'. Did you mean 'implements'? tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(45,18): error TS2508: No base constructor has the specified number of type arguments. tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(56,18): error TS2510: Base constructors must all have the same return type. @@ -12,7 +12,7 @@ tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts // Error, no Base constructor function class D0 extends Base { ~~~~ -!!! error TS2304: Cannot find name 'Base'. +!!! error TS2689: Cannot extend an interface 'Base'. Did you mean 'implements'? } interface BaseConstructor { diff --git a/tests/baselines/reference/classExtendsEveryObjectType.errors.txt b/tests/baselines/reference/classExtendsEveryObjectType.errors.txt index 0339dfad13a..e1c5137e3a9 100644 --- a/tests/baselines/reference/classExtendsEveryObjectType.errors.txt +++ b/tests/baselines/reference/classExtendsEveryObjectType.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(4,17): error TS2304: Cannot find name 'I'. +tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(4,17): error TS2689: Cannot extend an interface 'I'. Did you mean 'implements'? tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(6,18): error TS2507: Type '{ foo: any; }' is not a constructor function type. tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(6,25): error TS2304: Cannot find name 'string'. tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(6,31): error TS1005: ',' expected. @@ -14,7 +14,7 @@ tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/cla } class C extends I { } // error ~ -!!! error TS2304: Cannot find name 'I'. +!!! error TS2689: Cannot extend an interface 'I'. Did you mean 'implements'? class C2 extends { foo: string; } { } // error ~~~~~~~~~~~~~~~~ diff --git a/tests/baselines/reference/classExtendsInterface.errors.txt b/tests/baselines/reference/classExtendsInterface.errors.txt index 2ecca6cf2cf..72b8466105c 100644 --- a/tests/baselines/reference/classExtendsInterface.errors.txt +++ b/tests/baselines/reference/classExtendsInterface.errors.txt @@ -1,17 +1,17 @@ -tests/cases/compiler/classExtendsInterface.ts(2,17): error TS2304: Cannot find name 'Comparable'. -tests/cases/compiler/classExtendsInterface.ts(6,21): error TS2304: Cannot find name 'Comparable2'. +tests/cases/compiler/classExtendsInterface.ts(2,17): error TS2689: Cannot extend an interface 'Comparable'. Did you mean 'implements'? +tests/cases/compiler/classExtendsInterface.ts(6,21): error TS2689: Cannot extend an interface 'Comparable2'. Did you mean 'implements'? ==== tests/cases/compiler/classExtendsInterface.ts (2 errors) ==== interface Comparable {} class A extends Comparable {} ~~~~~~~~~~ -!!! error TS2304: Cannot find name 'Comparable'. +!!! error TS2689: Cannot extend an interface 'Comparable'. Did you mean 'implements'? class B implements Comparable {} - + interface Comparable2 {} class A2 extends Comparable2 {} ~~~~~~~~~~~ -!!! error TS2304: Cannot find name 'Comparable2'. +!!! error TS2689: Cannot extend an interface 'Comparable2'. Did you mean 'implements'? class B2 implements Comparable2 {} \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsInterface.js b/tests/baselines/reference/classExtendsInterface.js index c06c4e66d8b..b324f7382d8 100644 --- a/tests/baselines/reference/classExtendsInterface.js +++ b/tests/baselines/reference/classExtendsInterface.js @@ -2,7 +2,7 @@ interface Comparable {} class A extends Comparable {} class B implements Comparable {} - + interface Comparable2 {} class A2 extends Comparable2 {} class B2 implements Comparable2 {} diff --git a/tests/baselines/reference/classExtendsInterfaceInExpression.errors.txt b/tests/baselines/reference/classExtendsInterfaceInExpression.errors.txt new file mode 100644 index 00000000000..64b160ea766 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterfaceInExpression.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/classExtendsInterfaceInExpression.ts(7,25): error TS2304: Cannot find name 'A'. + + +==== tests/cases/compiler/classExtendsInterfaceInExpression.ts (1 errors) ==== + interface A {} + + function factory(a: any): {new(): Object} { + return null; + } + + class C extends factory(A) {} + ~ +!!! error TS2304: Cannot find name 'A'. + \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsInterfaceInExpression.js b/tests/baselines/reference/classExtendsInterfaceInExpression.js new file mode 100644 index 00000000000..69e63f92570 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterfaceInExpression.js @@ -0,0 +1,26 @@ +//// [classExtendsInterfaceInExpression.ts] +interface A {} + +function factory(a: any): {new(): Object} { + return null; +} + +class C extends factory(A) {} + + +//// [classExtendsInterfaceInExpression.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +function factory(a) { + return null; +} +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + } + return C; +}(factory(A))); diff --git a/tests/baselines/reference/classExtendsInterfaceInModule.errors.txt b/tests/baselines/reference/classExtendsInterfaceInModule.errors.txt new file mode 100644 index 00000000000..9a87ddf51b6 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterfaceInModule.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/classExtendsInterfaceInModule.ts(5,18): error TS2689: Cannot extend an interface 'M.I1'. Did you mean 'implements'? +tests/cases/compiler/classExtendsInterfaceInModule.ts(6,21): error TS2689: Cannot extend an interface 'M.I2'. Did you mean 'implements'? +tests/cases/compiler/classExtendsInterfaceInModule.ts(14,17): error TS2689: Cannot extend an interface 'Mod.Nested.I'. Did you mean 'implements'? + + +==== tests/cases/compiler/classExtendsInterfaceInModule.ts (3 errors) ==== + module M { + export interface I1 {} + export interface I2 {} + } + class C1 extends M.I1 {} + ~ +!!! error TS2689: Cannot extend an interface 'M.I1'. Did you mean 'implements'? + class C2 extends M.I2 {} + ~ +!!! error TS2689: Cannot extend an interface 'M.I2'. Did you mean 'implements'? + + module Mod { + export namespace Nested { + export interface I {} + } + } + + class D extends Mod.Nested.I {} + ~~~ +!!! error TS2689: Cannot extend an interface 'Mod.Nested.I'. Did you mean 'implements'? + \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsInterfaceInModule.js b/tests/baselines/reference/classExtendsInterfaceInModule.js new file mode 100644 index 00000000000..45415488700 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterfaceInModule.js @@ -0,0 +1,44 @@ +//// [classExtendsInterfaceInModule.ts] +module M { + export interface I1 {} + export interface I2 {} +} +class C1 extends M.I1 {} +class C2 extends M.I2 {} + +module Mod { + export namespace Nested { + export interface I {} + } +} + +class D extends Mod.Nested.I {} + + +//// [classExtendsInterfaceInModule.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var C1 = (function (_super) { + __extends(C1, _super); + function C1() { + _super.apply(this, arguments); + } + return C1; +}(M.I1)); +var C2 = (function (_super) { + __extends(C2, _super); + function C2() { + _super.apply(this, arguments); + } + return C2; +}(M.I2)); +var D = (function (_super) { + __extends(D, _super); + function D() { + _super.apply(this, arguments); + } + return D; +}(Mod.Nested.I)); diff --git a/tests/baselines/reference/genericTypeReferenceWithoutTypeArgument2.errors.txt b/tests/baselines/reference/genericTypeReferenceWithoutTypeArgument2.errors.txt index e590ad42a7d..b6767a3b938 100644 --- a/tests/baselines/reference/genericTypeReferenceWithoutTypeArgument2.errors.txt +++ b/tests/baselines/reference/genericTypeReferenceWithoutTypeArgument2.errors.txt @@ -13,7 +13,7 @@ tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenc tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(18,23): error TS2314: Generic type 'I' requires 1 type argument(s). tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(18,27): error TS2314: Generic type 'I' requires 1 type argument(s). tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(18,38): error TS2314: Generic type 'I' requires 1 type argument(s). -tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(20,17): error TS2304: Cannot find name 'I'. +tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(20,17): error TS2689: Cannot extend an interface 'I'. Did you mean 'implements'? tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(23,21): error TS2314: Generic type 'I' requires 1 type argument(s). tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(29,18): error TS2304: Cannot find name 'M'. tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenceWithoutTypeArgument2.ts(30,24): error TS2314: Generic type 'E' requires 1 type argument(s). @@ -76,7 +76,7 @@ tests/cases/conformance/types/specifyingTypes/typeReferences/genericTypeReferenc class D extends I { ~ -!!! error TS2304: Cannot find name 'I'. +!!! error TS2689: Cannot extend an interface 'I'. Did you mean 'implements'? } interface U extends I {} diff --git a/tests/cases/compiler/classExtendsInterface.ts b/tests/cases/compiler/classExtendsInterface.ts index 36f42167d1a..eaab1d60fc4 100644 --- a/tests/cases/compiler/classExtendsInterface.ts +++ b/tests/cases/compiler/classExtendsInterface.ts @@ -1,7 +1,7 @@ interface Comparable {} class A extends Comparable {} class B implements Comparable {} - + interface Comparable2 {} class A2 extends Comparable2 {} class B2 implements Comparable2 {} diff --git a/tests/cases/compiler/classExtendsInterfaceInExpression.ts b/tests/cases/compiler/classExtendsInterfaceInExpression.ts new file mode 100644 index 00000000000..002631afedf --- /dev/null +++ b/tests/cases/compiler/classExtendsInterfaceInExpression.ts @@ -0,0 +1,7 @@ +interface A {} + +function factory(a: any): {new(): Object} { + return null; +} + +class C extends factory(A) {} diff --git a/tests/cases/compiler/classExtendsInterfaceInModule.ts b/tests/cases/compiler/classExtendsInterfaceInModule.ts new file mode 100644 index 00000000000..4ea24e5dd7d --- /dev/null +++ b/tests/cases/compiler/classExtendsInterfaceInModule.ts @@ -0,0 +1,14 @@ +module M { + export interface I1 {} + export interface I2 {} +} +class C1 extends M.I1 {} +class C2 extends M.I2 {} + +module Mod { + export namespace Nested { + export interface I {} + } +} + +class D extends Mod.Nested.I {}