Skip to content

Commit 1d51dfa

Browse files
authored
improve error message for using property of type as type (microsoft#45354)
* improve error message for using property of type as type * suggest typeof when possible * fix naming and error location
1 parent d514a69 commit 1d51dfa

7 files changed

+73
-37
lines changed

src/compiler/checker.ts

+39-3
Original file line numberDiff line numberDiff line change
@@ -3156,6 +3156,30 @@ namespace ts {
31563156
return symbol.parent ? getFullyQualifiedName(symbol.parent, containingLocation) + "." + symbolToString(symbol) : symbolToString(symbol, containingLocation, /*meaning*/ undefined, SymbolFormatFlags.DoNotIncludeSymbolChain | SymbolFormatFlags.AllowAnyNodeKind);
31573157
}
31583158

3159+
function getContainingQualifiedNameNode(node: QualifiedName) {
3160+
while (isQualifiedName(node.parent)) {
3161+
node = node.parent;
3162+
}
3163+
return node;
3164+
}
3165+
3166+
function tryGetQualifiedNameAsValue(node: QualifiedName) {
3167+
let left: Identifier | QualifiedName = getFirstIdentifier(node);
3168+
let symbol = resolveName(left, left.escapedText, SymbolFlags.Value, undefined, left, /*isUse*/ true);
3169+
if (!symbol) {
3170+
return undefined;
3171+
}
3172+
while (isQualifiedName(left.parent)) {
3173+
const type = getTypeOfSymbol(symbol);
3174+
symbol = getPropertyOfType(type, left.parent.right.escapedText);
3175+
if (!symbol) {
3176+
return undefined;
3177+
}
3178+
left = left.parent;
3179+
}
3180+
return symbol;
3181+
}
3182+
31593183
/**
31603184
* Resolves a qualified name and any involved aliases.
31613185
*/
@@ -3205,10 +3229,22 @@ namespace ts {
32053229
if (!ignoreErrors) {
32063230
const namespaceName = getFullyQualifiedName(namespace);
32073231
const declarationName = declarationNameToString(right);
3208-
const suggestion = getSuggestedSymbolForNonexistentModule(right, namespace);
3209-
suggestion ?
3210-
error(right, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, symbolToString(suggestion)) :
3232+
const suggestionForNonexistentModule = getSuggestedSymbolForNonexistentModule(right, namespace);
3233+
const exportedTypeSymbol = getMergedSymbol(getSymbol(getExportsOfSymbol(namespace), right.escapedText, SymbolFlags.Type));
3234+
const containingQualifiedName = isQualifiedName(name) && getContainingQualifiedNameNode(name);
3235+
const canSuggestTypeof = containingQualifiedName && !isTypeOfExpression(containingQualifiedName.parent) && tryGetQualifiedNameAsValue(containingQualifiedName);
3236+
if (suggestionForNonexistentModule) {
3237+
error(right, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, symbolToString(suggestionForNonexistentModule));
3238+
}
3239+
else if (canSuggestTypeof) {
3240+
error(containingQualifiedName, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, entityNameToString(containingQualifiedName));
3241+
}
3242+
else if (meaning & SymbolFlags.Namespace && exportedTypeSymbol && isQualifiedName(name.parent)) {
3243+
error(name.parent.right, Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, symbolToString(exportedTypeSymbol), unescapeLeadingUnderscores(name.parent.right.escapedText));
3244+
}
3245+
else {
32113246
error(right, Diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName);
3247+
}
32123248
}
32133249
return undefined;
32143250
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(6,12): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
2-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(7,18): error TS2694: Namespace 'Test1' has no exported member 'Foo'.
2+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(7,22): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
33
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(15,12): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
4-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(16,18): error TS2694: Namespace 'Test2' has no exported member 'Foo'.
4+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(16,22): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
55
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(24,12): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
6-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(25,18): error TS2694: Namespace 'Test3' has no exported member 'Foo'.
6+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(25,22): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
77
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(32,12): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
8-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(33,18): error TS2694: Namespace 'Test4' has no exported member 'Foo'.
8+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(33,22): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
99
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(40,12): error TS2702: 'Foo' only refers to a type, but is being used as a namespace here.
10-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(41,18): error TS2694: Namespace 'Test5' has no exported member 'Foo'.
11-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,20): error TS2694: Namespace 'Test5' has no exported member 'Foo'.
10+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(41,22): error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
1211
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS1003: Identifier expected.
12+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS2713: Cannot access 'Foo.' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property '' in 'Foo' with 'Foo[""]'?
1313

1414

1515
==== tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts (12 errors) ====
@@ -22,8 +22,8 @@ tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS1003
2222
~~~~~~~
2323
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
2424
var y: Test1.Foo.bar = "";
25-
~~~
26-
!!! error TS2694: Namespace 'Test1' has no exported member 'Foo'.
25+
~~~
26+
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
2727
}
2828

2929
namespace Test2 {
@@ -35,8 +35,8 @@ tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS1003
3535
~~~~~~~
3636
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
3737
var y: Test2.Foo.bar = "";
38-
~~~
39-
!!! error TS2694: Namespace 'Test2' has no exported member 'Foo'.
38+
~~~
39+
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
4040
}
4141

4242
namespace Test3 {
@@ -48,8 +48,8 @@ tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS1003
4848
~~~~~~~
4949
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
5050
var y: Test3.Foo.bar = "";
51-
~~~
52-
!!! error TS2694: Namespace 'Test3' has no exported member 'Foo'.
51+
~~~
52+
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
5353
}
5454

5555
namespace Test4 {
@@ -60,8 +60,8 @@ tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS1003
6060
~~~~~~~
6161
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
6262
var y: Test4.Foo.bar = "";
63-
~~~
64-
!!! error TS2694: Namespace 'Test4' has no exported member 'Foo'.
63+
~~~
64+
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
6565
}
6666

6767
namespace Test5 {
@@ -72,12 +72,12 @@ tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts(44,24): error TS1003
7272
~~~
7373
!!! error TS2702: 'Foo' only refers to a type, but is being used as a namespace here.
7474
var y: Test5.Foo.bar = "";
75-
~~~
76-
!!! error TS2694: Namespace 'Test5' has no exported member 'Foo'.
75+
~~~
76+
!!! error TS2713: Cannot access 'Foo.bar' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property 'bar' in 'Foo' with 'Foo["bar"]'?
7777
}
7878

7979
import lol = Test5.Foo.
80-
~~~
81-
!!! error TS2694: Namespace 'Test5' has no exported member 'Foo'.
8280

83-
!!! error TS1003: Identifier expected.
81+
!!! error TS1003: Identifier expected.
82+
83+
!!! error TS2713: Cannot access 'Foo.' because 'Foo' is a type, but not a namespace. Did you mean to retrieve the type of the property '' in 'Foo' with 'Foo[""]'?

tests/baselines/reference/errorForUsingPropertyOfTypeAsType03.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts(11,19): error TS2694: Namespace 'Color' has no exported member 'Red'.
1+
tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts(11,13): error TS2749: 'Color.Red.toString' refers to a value, but is being used as a type here. Did you mean 'typeof Color.Red.toString'?
22
tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts(13,19): error TS2339: Property 'Red' does not exist on type 'Color'.
33
tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts(19,13): error TS2702: 'C1' only refers to a type, but is being used as a namespace here.
44
tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts(20,13): error TS2702: 'C1' only refers to a type, but is being used as a namespace here.
@@ -19,8 +19,8 @@ tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts(24,13): error TS2713
1919
type C2 = typeof Color;
2020

2121
let a1: Color.Red.toString;
22-
~~~
23-
!!! error TS2694: Namespace 'Color' has no exported member 'Red'.
22+
~~~~~~~~~~~~~~~~~~
23+
!!! error TS2749: 'Color.Red.toString' refers to a value, but is being used as a type here. Did you mean 'typeof Color.Red.toString'?
2424
let a2: Color.Red["toString"];
2525
let a3: Color["Red"]["toString"];
2626
~~~~~

tests/baselines/reference/genericFunduleInModule.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/genericFunduleInModule.ts(8,10): error TS2694: Namespace 'A' has no exported member 'B'.
1+
tests/cases/compiler/genericFunduleInModule.ts(8,8): error TS2749: 'A.B' refers to a value, but is being used as a type here. Did you mean 'typeof A.B'?
22

33

44
==== tests/cases/compiler/genericFunduleInModule.ts (1 errors) ====
@@ -10,6 +10,6 @@ tests/cases/compiler/genericFunduleInModule.ts(8,10): error TS2694: Namespace 'A
1010
}
1111

1212
var b: A.B;
13-
~
14-
!!! error TS2694: Namespace 'A' has no exported member 'B'.
13+
~~~
14+
!!! error TS2749: 'A.B' refers to a value, but is being used as a type here. Did you mean 'typeof A.B'?
1515
A.B(1);

tests/baselines/reference/genericFunduleInModule2.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/genericFunduleInModule2.ts(11,10): error TS2694: Namespace 'A' has no exported member 'B'.
1+
tests/cases/compiler/genericFunduleInModule2.ts(11,8): error TS2749: 'A.B' refers to a value, but is being used as a type here. Did you mean 'typeof A.B'?
22

33

44
==== tests/cases/compiler/genericFunduleInModule2.ts (1 errors) ====
@@ -13,6 +13,6 @@ tests/cases/compiler/genericFunduleInModule2.ts(11,10): error TS2694: Namespace
1313
}
1414

1515
var b: A.B;
16-
~
17-
!!! error TS2694: Namespace 'A' has no exported member 'B'.
16+
~~~
17+
!!! error TS2749: 'A.B' refers to a value, but is being used as a type here. Did you mean 'typeof A.B'?
1818
A.B(1);

tests/baselines/reference/importClause_namespaceImport.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/b.ts(2,1): error TS1361: 'types' cannot be used as a value because it was imported using 'import type'.
22
/b.ts(3,1): error TS1361: 'types' cannot be used as a value because it was imported using 'import type'.
3-
/b.ts(4,14): error TS2694: Namespace '"/a"' has no exported member 'Value'.
3+
/b.ts(4,8): error TS2749: 'types.Value' refers to a value, but is being used as a type here. Did you mean 'typeof types.Value'?
44
/b.ts(5,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'.
55
/b.ts(6,7): error TS2741: Property 'b' is missing in type '{}' but required in type 'B'.
66
/b.ts(8,13): error TS1361: 'types' cannot be used as a value because it was imported using 'import type'.
@@ -23,8 +23,8 @@
2323
!!! error TS1361: 'types' cannot be used as a value because it was imported using 'import type'.
2424
!!! related TS1376 /b.ts:1:18: 'types' was imported here.
2525
let v: types.Value;
26-
~~~~~
27-
!!! error TS2694: Namespace '"/a"' has no exported member 'Value'.
26+
~~~~~~~~~~~
27+
!!! error TS2749: 'types.Value' refers to a value, but is being used as a type here. Did you mean 'typeof types.Value'?
2828
const a: types.A = {};
2929
~
3030
!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'.

tests/baselines/reference/parserharness.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(16,21): er
22
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(17,21): error TS6053: File 'tests/cases/conformance/parser/ecmascript5/compiler/typescript.ts' not found.
33
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(18,21): error TS6053: File 'tests/cases/conformance/parser/ecmascript5/services/typescriptServices.ts' not found.
44
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(19,21): error TS6053: File 'tests/cases/conformance/parser/ecmascript5/RealWorld/diff.ts' not found.
5-
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(21,29): error TS2694: Namespace 'Harness' has no exported member 'Assert'.
5+
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(21,21): error TS2749: 'Harness.Assert' refers to a value, but is being used as a type here. Did you mean 'typeof Harness.Assert'?
66
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(25,17): error TS2304: Cannot find name 'IIO'.
77
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(41,12): error TS2304: Cannot find name 'ActiveXObject'.
88
tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(43,19): error TS2580: Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.
@@ -140,8 +140,8 @@ tests/cases/conformance/parser/ecmascript5/RealWorld/parserharness.ts(2030,32):
140140
!!! error TS6053: File 'tests/cases/conformance/parser/ecmascript5/RealWorld/diff.ts' not found.
141141

142142
declare var assert: Harness.Assert;
143-
~~~~~~
144-
!!! error TS2694: Namespace 'Harness' has no exported member 'Assert'.
143+
~~~~~~~~~~~~~~
144+
!!! error TS2749: 'Harness.Assert' refers to a value, but is being used as a type here. Did you mean 'typeof Harness.Assert'?
145145
declare var it;
146146
declare var describe;
147147
declare var run;

0 commit comments

Comments
 (0)