Skip to content

Commit 37b9a6b

Browse files
a-tarasyuksandersn
authored andcommitted
25840 - Add a more meaningful error message to the case when calling a public static method on an instance (#25922)
* add a more meaningful error message to the case when calling a public static method on an instance * Fix tests
1 parent d8cbe34 commit 37b9a6b

20 files changed

+208
-48
lines changed

src/compiler/checker.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,7 +3322,6 @@ namespace ts {
33223322
// Anonymous types without a symbol are never circular.
33233323
return createTypeNodeFromObjectType(type);
33243324
}
3325-
33263325
function shouldWriteTypeOfFunctionSymbol() {
33273326
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method
33283327
some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static));
@@ -9158,7 +9157,10 @@ namespace ts {
91589157
}
91599158
if (accessExpression && !isConstEnumObjectType(objectType)) {
91609159
if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
9161-
if (getIndexTypeOfType(objectType, IndexKind.Number)) {
9160+
if (propName !== undefined && typeHasStaticProperty(propName, objectType)) {
9161+
error(accessExpression, Diagnostics.Property_0_is_a_static_member_of_type_1, propName as string, typeToString(objectType));
9162+
}
9163+
else if (getIndexTypeOfType(objectType, IndexKind.Number)) {
91629164
error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
91639165
}
91649166
else {
@@ -18055,29 +18057,38 @@ namespace ts {
1805518057
}
1805618058
}
1805718059
}
18058-
const promisedType = getPromisedTypeOfPromise(containingType);
18059-
if (promisedType && getPropertyOfType(promisedType, propNode.escapedText)) {
18060-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await, declarationNameToString(propNode), typeToString(containingType));
18060+
if (typeHasStaticProperty(propNode.escapedText, containingType)) {
18061+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_is_a_static_member_of_type_1, declarationNameToString(propNode), typeToString(containingType));
1806118062
}
1806218063
else {
18063-
const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType);
18064-
if (suggestion !== undefined) {
18065-
const suggestedName = symbolName(suggestion);
18066-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName);
18067-
relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName);
18064+
const promisedType = getPromisedTypeOfPromise(containingType);
18065+
if (promisedType && getPropertyOfType(promisedType, propNode.escapedText)) {
18066+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await, declarationNameToString(propNode), typeToString(containingType));
1806818067
}
1806918068
else {
18070-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
18069+
const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType);
18070+
if (suggestion !== undefined) {
18071+
const suggestedName = symbolName(suggestion);
18072+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName);
18073+
relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName);
18074+
}
18075+
else {
18076+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
18077+
}
1807118078
}
1807218079
}
18073-
1807418080
const resultDiagnostic = createDiagnosticForNodeFromMessageChain(propNode, errorInfo);
1807518081
if (relatedInfo) {
1807618082
addRelatedInfo(resultDiagnostic, relatedInfo);
1807718083
}
1807818084
diagnostics.add(resultDiagnostic);
1807918085
}
1808018086

18087+
function typeHasStaticProperty(propName: __String, containingType: Type): boolean {
18088+
const prop = containingType.symbol && getPropertyOfType(getTypeOfSymbol(containingType.symbol), propName);
18089+
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
18090+
}
18091+
1808118092
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
1808218093
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1808318094
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,6 +2064,10 @@
20642064
"category": "Error",
20652065
"code": 2575
20662066
},
2067+
"Property '{0}' is a static member of type '{1}'": {
2068+
"category": "Error",
2069+
"code": 2576
2070+
},
20672071
"JSX element attributes type '{0}' may not be a union type.": {
20682072
"category": "Error",
20692073
"code": 2600

tests/baselines/reference/classImplementsClass6.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
tests/cases/compiler/classImplementsClass6.ts(20,3): error TS2339: Property 'bar' does not exist on type 'C'.
2-
tests/cases/compiler/classImplementsClass6.ts(21,4): error TS2339: Property 'bar' does not exist on type 'C2'.
2+
tests/cases/compiler/classImplementsClass6.ts(21,4): error TS2576: Property 'bar' is a static member of type 'C2'
33

44

55
==== tests/cases/compiler/classImplementsClass6.ts (2 errors) ====
@@ -27,4 +27,4 @@ tests/cases/compiler/classImplementsClass6.ts(21,4): error TS2339: Property 'bar
2727
!!! error TS2339: Property 'bar' does not exist on type 'C'.
2828
c2.bar(); // should error
2929
~~~
30-
!!! error TS2339: Property 'bar' does not exist on type 'C2'.
30+
!!! error TS2576: Property 'bar' is a static member of type 'C2'

tests/baselines/reference/classSideInheritance1.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
tests/cases/compiler/classSideInheritance1.ts(12,3): error TS2339: Property 'bar' does not exist on type 'A'.
2-
tests/cases/compiler/classSideInheritance1.ts(13,3): error TS2339: Property 'bar' does not exist on type 'C2'.
1+
tests/cases/compiler/classSideInheritance1.ts(12,3): error TS2576: Property 'bar' is a static member of type 'A'
2+
tests/cases/compiler/classSideInheritance1.ts(13,3): error TS2576: Property 'bar' is a static member of type 'C2'
33

44

55
==== tests/cases/compiler/classSideInheritance1.ts (2 errors) ====
@@ -16,9 +16,9 @@ tests/cases/compiler/classSideInheritance1.ts(13,3): error TS2339: Property 'bar
1616
var c: C2;
1717
a.bar(); // static off an instance - should be an error
1818
~~~
19-
!!! error TS2339: Property 'bar' does not exist on type 'A'.
19+
!!! error TS2576: Property 'bar' is a static member of type 'A'
2020
c.bar(); // static off an instance - should be an error
2121
~~~
22-
!!! error TS2339: Property 'bar' does not exist on type 'C2'.
22+
!!! error TS2576: Property 'bar' is a static member of type 'C2'
2323
A.bar(); // valid
2424
C2.bar(); // valid
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
tests/cases/compiler/classStaticPropertyAccess.ts(9,1): error TS2576: Property 'y' is a static member of type 'A'
2+
tests/cases/compiler/classStaticPropertyAccess.ts(10,3): error TS2576: Property 'y' is a static member of type 'A'
3+
tests/cases/compiler/classStaticPropertyAccess.ts(11,3): error TS2341: Property '_b' is private and only accessible within class 'A'.
4+
tests/cases/compiler/classStaticPropertyAccess.ts(12,3): error TS2339: Property 'a' does not exist on type 'typeof A'.
5+
6+
7+
==== tests/cases/compiler/classStaticPropertyAccess.ts (4 errors) ====
8+
class A {
9+
public static x: number = 1;
10+
public static y: number = 1;
11+
private static _b: number = 2;
12+
}
13+
14+
const a = new A();
15+
16+
a['y'] // Error
17+
~~~~~~
18+
!!! error TS2576: Property 'y' is a static member of type 'A'
19+
a.y // Error
20+
~
21+
!!! error TS2576: Property 'y' is a static member of type 'A'
22+
A._b // Error
23+
~~
24+
!!! error TS2341: Property '_b' is private and only accessible within class 'A'.
25+
A.a
26+
~
27+
!!! error TS2339: Property 'a' does not exist on type 'typeof A'.
28+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [classStaticPropertyAccess.ts]
2+
class A {
3+
public static x: number = 1;
4+
public static y: number = 1;
5+
private static _b: number = 2;
6+
}
7+
8+
const a = new A();
9+
10+
a['y'] // Error
11+
a.y // Error
12+
A._b // Error
13+
A.a
14+
15+
16+
//// [classStaticPropertyAccess.js]
17+
"use strict";
18+
var A = /** @class */ (function () {
19+
function A() {
20+
}
21+
A.x = 1;
22+
A.y = 1;
23+
A._b = 2;
24+
return A;
25+
}());
26+
var a = new A();
27+
a['y']; // Error
28+
a.y; // Error
29+
A._b; // Error
30+
A.a;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/compiler/classStaticPropertyAccess.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(classStaticPropertyAccess.ts, 0, 0))
4+
5+
public static x: number = 1;
6+
>x : Symbol(A.x, Decl(classStaticPropertyAccess.ts, 0, 9))
7+
8+
public static y: number = 1;
9+
>y : Symbol(A.y, Decl(classStaticPropertyAccess.ts, 1, 32))
10+
11+
private static _b: number = 2;
12+
>_b : Symbol(A._b, Decl(classStaticPropertyAccess.ts, 2, 32))
13+
}
14+
15+
const a = new A();
16+
>a : Symbol(a, Decl(classStaticPropertyAccess.ts, 6, 5))
17+
>A : Symbol(A, Decl(classStaticPropertyAccess.ts, 0, 0))
18+
19+
a['y'] // Error
20+
>a : Symbol(a, Decl(classStaticPropertyAccess.ts, 6, 5))
21+
22+
a.y // Error
23+
>a : Symbol(a, Decl(classStaticPropertyAccess.ts, 6, 5))
24+
25+
A._b // Error
26+
>A._b : Symbol(A._b, Decl(classStaticPropertyAccess.ts, 2, 32))
27+
>A : Symbol(A, Decl(classStaticPropertyAccess.ts, 0, 0))
28+
>_b : Symbol(A._b, Decl(classStaticPropertyAccess.ts, 2, 32))
29+
30+
A.a
31+
>A : Symbol(A, Decl(classStaticPropertyAccess.ts, 0, 0))
32+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/compiler/classStaticPropertyAccess.ts ===
2+
class A {
3+
>A : A
4+
5+
public static x: number = 1;
6+
>x : number
7+
>1 : 1
8+
9+
public static y: number = 1;
10+
>y : number
11+
>1 : 1
12+
13+
private static _b: number = 2;
14+
>_b : number
15+
>2 : 2
16+
}
17+
18+
const a = new A();
19+
>a : A
20+
>new A() : A
21+
>A : typeof A
22+
23+
a['y'] // Error
24+
>a['y'] : any
25+
>a : A
26+
>'y' : "y"
27+
28+
a.y // Error
29+
>a.y : any
30+
>a : A
31+
>y : any
32+
33+
A._b // Error
34+
>A._b : number
35+
>A : typeof A
36+
>_b : number
37+
38+
A.a
39+
>A.a : any
40+
>A : typeof A
41+
>a : any
42+

tests/baselines/reference/cloduleTest2.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
tests/cases/compiler/cloduleTest2.ts(4,13): error TS2554: Expected 1 arguments, but got 0.
22
tests/cases/compiler/cloduleTest2.ts(10,13): error TS2554: Expected 1 arguments, but got 0.
3-
tests/cases/compiler/cloduleTest2.ts(18,7): error TS2339: Property 'bar' does not exist on type 'm3d'.
3+
tests/cases/compiler/cloduleTest2.ts(18,7): error TS2576: Property 'bar' is a static member of type 'm3d'
44
tests/cases/compiler/cloduleTest2.ts(19,7): error TS2339: Property 'y' does not exist on type 'm3d'.
5-
tests/cases/compiler/cloduleTest2.ts(27,7): error TS2339: Property 'bar' does not exist on type 'm3d'.
5+
tests/cases/compiler/cloduleTest2.ts(27,7): error TS2576: Property 'bar' is a static member of type 'm3d'
66
tests/cases/compiler/cloduleTest2.ts(28,7): error TS2339: Property 'y' does not exist on type 'm3d'.
77
tests/cases/compiler/cloduleTest2.ts(33,9): error TS2554: Expected 1 arguments, but got 0.
88
tests/cases/compiler/cloduleTest2.ts(36,10): error TS2554: Expected 1 arguments, but got 0.
@@ -32,7 +32,7 @@ tests/cases/compiler/cloduleTest2.ts(36,10): error TS2554: Expected 1 arguments,
3232
r.foo();
3333
r.bar(); // error
3434
~~~
35-
!!! error TS2339: Property 'bar' does not exist on type 'm3d'.
35+
!!! error TS2576: Property 'bar' is a static member of type 'm3d'
3636
r.y; // error
3737
~
3838
!!! error TS2339: Property 'y' does not exist on type 'm3d'.
@@ -45,7 +45,7 @@ tests/cases/compiler/cloduleTest2.ts(36,10): error TS2554: Expected 1 arguments,
4545
r.foo();
4646
r.bar(); // error
4747
~~~
48-
!!! error TS2339: Property 'bar' does not exist on type 'm3d'.
48+
!!! error TS2576: Property 'bar' is a static member of type 'm3d'
4949
r.y; // error
5050
~
5151
!!! error TS2339: Property 'y' does not exist on type 'm3d'.

tests/baselines/reference/staticMemberExportAccess.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
tests/cases/compiler/staticMemberExportAccess.ts(14,35): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
2-
tests/cases/compiler/staticMemberExportAccess.ts(17,18): error TS2339: Property 'bar' does not exist on type 'Sammy'.
2+
tests/cases/compiler/staticMemberExportAccess.ts(17,18): error TS2576: Property 'bar' is a static member of type 'Sammy'
33
tests/cases/compiler/staticMemberExportAccess.ts(18,18): error TS2339: Property 'x' does not exist on type 'Sammy'.
44

55

@@ -24,7 +24,7 @@ tests/cases/compiler/staticMemberExportAccess.ts(18,18): error TS2339: Property
2424
var r2 = $.sammy.foo();
2525
var r3 = $.sammy.bar(); // error
2626
~~~
27-
!!! error TS2339: Property 'bar' does not exist on type 'Sammy'.
27+
!!! error TS2576: Property 'bar' is a static member of type 'Sammy'
2828
var r4 = $.sammy.x; // error
2929
~
3030
!!! error TS2339: Property 'x' does not exist on type 'Sammy'.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
tests/cases/compiler/staticOffOfInstance1.ts(3,10): error TS2339: Property 'Foo' does not exist on type 'List'.
1+
tests/cases/compiler/staticOffOfInstance1.ts(3,10): error TS2576: Property 'Foo' is a static member of type 'List'
22

33

44
==== tests/cases/compiler/staticOffOfInstance1.ts (1 errors) ====
55
class List {
66
public Blah() {
77
this.Foo();
88
~~~
9-
!!! error TS2339: Property 'Foo' does not exist on type 'List'.
9+
!!! error TS2576: Property 'Foo' is a static member of type 'List'
1010
}
1111
public static Foo() {}
1212
}

tests/baselines/reference/staticOffOfInstance2.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
tests/cases/compiler/staticOffOfInstance2.ts(3,14): error TS2339: Property 'Foo' does not exist on type 'List<T>'.
1+
tests/cases/compiler/staticOffOfInstance2.ts(3,14): error TS2576: Property 'Foo' is a static member of type 'List<T>'
22

33

44
==== tests/cases/compiler/staticOffOfInstance2.ts (1 errors) ====
55
class List<T> {
66
public Blah() {
77
this.Foo(); // no error
88
~~~
9-
!!! error TS2339: Property 'Foo' does not exist on type 'List<T>'.
9+
!!! error TS2576: Property 'Foo' is a static member of type 'List<T>'
1010
List.Foo();
1111
}
1212
public static Foo() { }

tests/baselines/reference/staticPropertyNotInClassType.errors.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(4,20): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
22
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(5,20): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
3-
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(16,16): error TS2339: Property 'foo' does not exist on type 'C'.
3+
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(16,16): error TS2576: Property 'foo' is a static member of type 'C'
44
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(17,16): error TS2339: Property 'bar' does not exist on type 'C'.
5-
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(18,16): error TS2339: Property 'x' does not exist on type 'C'.
5+
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(18,16): error TS2576: Property 'x' is a static member of type 'C'
66
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(24,20): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
77
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(25,20): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
88
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(27,21): error TS2302: Static members cannot reference class type parameters.
9-
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(36,16): error TS2339: Property 'foo' does not exist on type 'C<number, string>'.
9+
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(36,16): error TS2576: Property 'foo' is a static member of type 'C<number, string>'
1010
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(37,16): error TS2339: Property 'bar' does not exist on type 'C<number, string>'.
11-
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(38,16): error TS2339: Property 'x' does not exist on type 'C<number, string>'.
11+
tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts(38,16): error TS2576: Property 'x' is a static member of type 'C<number, string>'
1212

1313

1414
==== tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.ts (11 errors) ====
@@ -33,13 +33,13 @@ tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.
3333
var r = c.fn();
3434
var r4 = c.foo; // error
3535
~~~
36-
!!! error TS2339: Property 'foo' does not exist on type 'C'.
36+
!!! error TS2576: Property 'foo' is a static member of type 'C'
3737
var r5 = c.bar; // error
3838
~~~
3939
!!! error TS2339: Property 'bar' does not exist on type 'C'.
4040
var r6 = c.x; // error
4141
~
42-
!!! error TS2339: Property 'x' does not exist on type 'C'.
42+
!!! error TS2576: Property 'x' is a static member of type 'C'
4343
}
4444

4545
module Generic {
@@ -65,11 +65,11 @@ tests/cases/conformance/classes/members/classTypes/staticPropertyNotInClassType.
6565
var r = c.fn();
6666
var r4 = c.foo; // error
6767
~~~
68-
!!! error TS2339: Property 'foo' does not exist on type 'C<number, string>'.
68+
!!! error TS2576: Property 'foo' is a static member of type 'C<number, string>'
6969
var r5 = c.bar; // error
7070
~~~
7171
!!! error TS2339: Property 'bar' does not exist on type 'C<number, string>'.
7272
var r6 = c.x; // error
7373
~
74-
!!! error TS2339: Property 'x' does not exist on type 'C<number, string>'.
74+
!!! error TS2576: Property 'x' is a static member of type 'C<number, string>'
7575
}

tests/baselines/reference/superAccess.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/superAccess.ts(9,24): error TS2339: Property 'S1' does not exist on type 'MyBase'.
1+
tests/cases/compiler/superAccess.ts(9,24): error TS2576: Property 'S1' is a static member of type 'MyBase'
22
tests/cases/compiler/superAccess.ts(10,24): error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.
33
tests/cases/compiler/superAccess.ts(11,24): error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.
44

@@ -14,7 +14,7 @@ tests/cases/compiler/superAccess.ts(11,24): error TS2340: Only public and protec
1414
foo() {
1515
var l3 = super.S1; // Expected => Error: Only public instance methods of the base class are accessible via the 'super' keyword
1616
~~
17-
!!! error TS2339: Property 'S1' does not exist on type 'MyBase'.
17+
!!! error TS2576: Property 'S1' is a static member of type 'MyBase'
1818
var l4 = super.S2; // Expected => Error: Only public instance methods of the base class are accessible via the 'super' keyword
1919
~~
2020
!!! error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.

0 commit comments

Comments
 (0)