Skip to content

Commit 2b4378d

Browse files
authored
Merge pull request microsoft#9180 from HerringtonDarkholme/interface
[Fix microsoft#9004] Improve error message for extending interface
2 parents abe7c19 + 2a9636b commit 2b4378d

14 files changed

Lines changed: 177 additions & 15 deletions

src/compiler/checker.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,8 @@ namespace ts {
853853

854854
if (!result) {
855855
if (nameNotFoundMessage) {
856-
if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg)) {
856+
if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) &&
857+
!checkAndReportErrorForExtendingInterface(errorLocation)) {
857858
error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
858859
}
859860
}
@@ -937,6 +938,31 @@ namespace ts {
937938
return false;
938939
}
939940

941+
942+
function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
943+
let parentClassExpression = errorLocation;
944+
while (parentClassExpression) {
945+
const kind = parentClassExpression.kind;
946+
if (kind === SyntaxKind.Identifier || kind === SyntaxKind.PropertyAccessExpression) {
947+
parentClassExpression = parentClassExpression.parent;
948+
continue;
949+
}
950+
if (kind === SyntaxKind.ExpressionWithTypeArguments) {
951+
break;
952+
}
953+
return false;
954+
}
955+
if (!parentClassExpression) {
956+
return false;
957+
}
958+
const expression = (<ExpressionWithTypeArguments>parentClassExpression).expression;
959+
if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) {
960+
error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression));
961+
return true;
962+
}
963+
return false;
964+
}
965+
940966
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
941967
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0);
942968
// Block-scoped variables cannot be used before their definition
@@ -10137,7 +10163,7 @@ namespace ts {
1013710163
}
1013810164
const prop = getPropertyOfType(apparentType, right.text);
1013910165
if (!prop) {
10140-
if (right.text) {
10166+
if (right.text && !checkAndReportErrorForExtendingInterface(node)) {
1014110167
error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type.flags & TypeFlags.ThisType ? apparentType : type));
1014210168
}
1014310169
return unknownType;

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,10 @@
19351935
"category": "Error",
19361936
"code": 2688
19371937
},
1938+
"Cannot extend an interface '{0}'. Did you mean 'implements'?": {
1939+
"category": "Error",
1940+
"code": 2689
1941+
},
19381942
"Import declaration '{0}' is using private name '{1}'.": {
19391943
"category": "Error",
19401944
"code": 4000

tests/baselines/reference/classExtendingClassLikeType.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(7,18): error TS2304: Cannot find name 'Base'.
1+
tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(7,18): error TS2689: Cannot extend an interface 'Base'. Did you mean 'implements'?
22
tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(45,18): error TS2508: No base constructor has the specified number of type arguments.
33
tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts(56,18): error TS2510: Base constructors must all have the same return type.
44

@@ -12,7 +12,7 @@ tests/cases/conformance/classes/classDeclarations/classExtendingClassLikeType.ts
1212
// Error, no Base constructor function
1313
class D0 extends Base<string, string> {
1414
~~~~
15-
!!! error TS2304: Cannot find name 'Base'.
15+
!!! error TS2689: Cannot extend an interface 'Base'. Did you mean 'implements'?
1616
}
1717

1818
interface BaseConstructor {

tests/baselines/reference/classExtendsEveryObjectType.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(4,17): error TS2304: Cannot find name 'I'.
1+
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(4,17): error TS2689: Cannot extend an interface 'I'. Did you mean 'implements'?
22
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(6,18): error TS2507: Type '{ foo: any; }' is not a constructor function type.
33
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(6,25): error TS2304: Cannot find name 'string'.
44
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsEveryObjectType.ts(6,31): error TS1005: ',' expected.
@@ -14,7 +14,7 @@ tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/cla
1414
}
1515
class C extends I { } // error
1616
~
17-
!!! error TS2304: Cannot find name 'I'.
17+
!!! error TS2689: Cannot extend an interface 'I'. Did you mean 'implements'?
1818

1919
class C2 extends { foo: string; } { } // error
2020
~~~~~~~~~~~~~~~~
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
tests/cases/compiler/classExtendsInterface.ts(2,17): error TS2304: Cannot find name 'Comparable'.
2-
tests/cases/compiler/classExtendsInterface.ts(6,21): error TS2304: Cannot find name 'Comparable2'.
1+
tests/cases/compiler/classExtendsInterface.ts(2,17): error TS2689: Cannot extend an interface 'Comparable'. Did you mean 'implements'?
2+
tests/cases/compiler/classExtendsInterface.ts(6,21): error TS2689: Cannot extend an interface 'Comparable2'. Did you mean 'implements'?
33

44

55
==== tests/cases/compiler/classExtendsInterface.ts (2 errors) ====
66
interface Comparable {}
77
class A extends Comparable {}
88
~~~~~~~~~~
9-
!!! error TS2304: Cannot find name 'Comparable'.
9+
!!! error TS2689: Cannot extend an interface 'Comparable'. Did you mean 'implements'?
1010
class B implements Comparable {}
11-
11+
1212
interface Comparable2<T> {}
1313
class A2<T> extends Comparable2<T> {}
1414
~~~~~~~~~~~
15-
!!! error TS2304: Cannot find name 'Comparable2'.
15+
!!! error TS2689: Cannot extend an interface 'Comparable2'. Did you mean 'implements'?
1616
class B2<T> implements Comparable2<T> {}
1717

tests/baselines/reference/classExtendsInterface.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
interface Comparable {}
33
class A extends Comparable {}
44
class B implements Comparable {}
5-
5+
66
interface Comparable2<T> {}
77
class A2<T> extends Comparable2<T> {}
88
class B2<T> implements Comparable2<T> {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/compiler/classExtendsInterfaceInExpression.ts(7,25): error TS2304: Cannot find name 'A'.
2+
3+
4+
==== tests/cases/compiler/classExtendsInterfaceInExpression.ts (1 errors) ====
5+
interface A {}
6+
7+
function factory(a: any): {new(): Object} {
8+
return null;
9+
}
10+
11+
class C extends factory(A) {}
12+
~
13+
!!! error TS2304: Cannot find name 'A'.
14+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [classExtendsInterfaceInExpression.ts]
2+
interface A {}
3+
4+
function factory(a: any): {new(): Object} {
5+
return null;
6+
}
7+
8+
class C extends factory(A) {}
9+
10+
11+
//// [classExtendsInterfaceInExpression.js]
12+
var __extends = (this && this.__extends) || function (d, b) {
13+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
14+
function __() { this.constructor = d; }
15+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
16+
};
17+
function factory(a) {
18+
return null;
19+
}
20+
var C = (function (_super) {
21+
__extends(C, _super);
22+
function C() {
23+
_super.apply(this, arguments);
24+
}
25+
return C;
26+
}(factory(A)));
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
tests/cases/compiler/classExtendsInterfaceInModule.ts(5,18): error TS2689: Cannot extend an interface 'M.I1'. Did you mean 'implements'?
2+
tests/cases/compiler/classExtendsInterfaceInModule.ts(6,21): error TS2689: Cannot extend an interface 'M.I2'. Did you mean 'implements'?
3+
tests/cases/compiler/classExtendsInterfaceInModule.ts(14,17): error TS2689: Cannot extend an interface 'Mod.Nested.I'. Did you mean 'implements'?
4+
5+
6+
==== tests/cases/compiler/classExtendsInterfaceInModule.ts (3 errors) ====
7+
module M {
8+
export interface I1 {}
9+
export interface I2<T> {}
10+
}
11+
class C1 extends M.I1 {}
12+
~
13+
!!! error TS2689: Cannot extend an interface 'M.I1'. Did you mean 'implements'?
14+
class C2<T> extends M.I2<T> {}
15+
~
16+
!!! error TS2689: Cannot extend an interface 'M.I2'. Did you mean 'implements'?
17+
18+
module Mod {
19+
export namespace Nested {
20+
export interface I {}
21+
}
22+
}
23+
24+
class D extends Mod.Nested.I {}
25+
~~~
26+
!!! error TS2689: Cannot extend an interface 'Mod.Nested.I'. Did you mean 'implements'?
27+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//// [classExtendsInterfaceInModule.ts]
2+
module M {
3+
export interface I1 {}
4+
export interface I2<T> {}
5+
}
6+
class C1 extends M.I1 {}
7+
class C2<T> extends M.I2<T> {}
8+
9+
module Mod {
10+
export namespace Nested {
11+
export interface I {}
12+
}
13+
}
14+
15+
class D extends Mod.Nested.I {}
16+
17+
18+
//// [classExtendsInterfaceInModule.js]
19+
var __extends = (this && this.__extends) || function (d, b) {
20+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
21+
function __() { this.constructor = d; }
22+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
23+
};
24+
var C1 = (function (_super) {
25+
__extends(C1, _super);
26+
function C1() {
27+
_super.apply(this, arguments);
28+
}
29+
return C1;
30+
}(M.I1));
31+
var C2 = (function (_super) {
32+
__extends(C2, _super);
33+
function C2() {
34+
_super.apply(this, arguments);
35+
}
36+
return C2;
37+
}(M.I2));
38+
var D = (function (_super) {
39+
__extends(D, _super);
40+
function D() {
41+
_super.apply(this, arguments);
42+
}
43+
return D;
44+
}(Mod.Nested.I));

0 commit comments

Comments
 (0)