Skip to content

Commit 38eccba

Browse files
a-tarasyukDanielRosenwasser
authored andcommitted
feat(29624): better errors for non-exported types (#36187)
1 parent 342f4c0 commit 38eccba

13 files changed

+175
-6
lines changed

src/compiler/checker.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2355,7 +2355,7 @@ namespace ts {
23552355
}
23562356
}
23572357
else {
2358-
if (moduleSymbol.exports && moduleSymbol.exports.has(InternalSymbolName.Default)) {
2358+
if (moduleSymbol.exports?.has(InternalSymbolName.Default)) {
23592359
error(
23602360
name,
23612361
Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead,
@@ -2364,7 +2364,7 @@ namespace ts {
23642364
);
23652365
}
23662366
else {
2367-
error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName);
2367+
reportNonExportedMember(name, declarationName, moduleSymbol, moduleName);
23682368
}
23692369
}
23702370
}
@@ -2373,6 +2373,27 @@ namespace ts {
23732373
}
23742374
}
23752375

2376+
function reportNonExportedMember(name: Identifier, declarationName: string, moduleSymbol: Symbol, moduleName: string): void {
2377+
const localSymbol = moduleSymbol.valueDeclaration.locals?.get(name.escapedText);
2378+
const exports = moduleSymbol.exports;
2379+
2380+
if (localSymbol) {
2381+
const exportedSymbol = exports && !exports.has(InternalSymbolName.ExportEquals)
2382+
? find(symbolsToArray(exports), symbol => !!getSymbolIfSameReference(symbol, localSymbol))
2383+
: undefined;
2384+
const diagnostic = exportedSymbol
2385+
? error(name, Diagnostics.Module_0_declares_1_locally_but_it_is_exported_as_2, moduleName, declarationName, symbolToString(exportedSymbol))
2386+
: error(name, Diagnostics.Module_0_declares_1_locally_but_it_is_not_exported, moduleName, declarationName);
2387+
2388+
addRelatedInfo(diagnostic,
2389+
createDiagnosticForNode(localSymbol.valueDeclaration, Diagnostics._0_is_declared_here, declarationName)
2390+
);
2391+
}
2392+
else {
2393+
error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName);
2394+
}
2395+
}
2396+
23762397
function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined {
23772398
const resolved = getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias);
23782399
if (resolved && node.parent.parent.isTypeOnly) {

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,14 @@
17641764
"category": "Error",
17651765
"code": 2458
17661766
},
1767+
"Module '{0}' declares '{1}' locally, but it is not exported.": {
1768+
"category": "Error",
1769+
"code": 2459
1770+
},
1771+
"Module '{0}' declares '{1}' locally, but it is exported as '{2}'.": {
1772+
"category": "Error",
1773+
"code": 2460
1774+
},
17671775
"Type '{0}' is not an array type.": {
17681776
"category": "Error",
17691777
"code": 2461
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
tests/cases/compiler/es6ImportNamedImportNoNamedExports_1.ts(1,10): error TS2305: Module '"./es6ImportNamedImportNoNamedExports_0"' has no exported member 'a'.
2-
tests/cases/compiler/es6ImportNamedImportNoNamedExports_1.ts(2,10): error TS2305: Module '"./es6ImportNamedImportNoNamedExports_0"' has no exported member 'a'.
1+
tests/cases/compiler/es6ImportNamedImportNoNamedExports_1.ts(1,10): error TS2459: Module '"./es6ImportNamedImportNoNamedExports_0"' declares 'a' locally, but it is not exported.
2+
tests/cases/compiler/es6ImportNamedImportNoNamedExports_1.ts(2,10): error TS2459: Module '"./es6ImportNamedImportNoNamedExports_0"' declares 'a' locally, but it is not exported.
33

44

55
==== tests/cases/compiler/es6ImportNamedImportNoNamedExports_0.ts (0 errors) ====
@@ -9,7 +9,9 @@ tests/cases/compiler/es6ImportNamedImportNoNamedExports_1.ts(2,10): error TS2305
99
==== tests/cases/compiler/es6ImportNamedImportNoNamedExports_1.ts (2 errors) ====
1010
import { a } from "./es6ImportNamedImportNoNamedExports_0";
1111
~
12-
!!! error TS2305: Module '"./es6ImportNamedImportNoNamedExports_0"' has no exported member 'a'.
12+
!!! error TS2459: Module '"./es6ImportNamedImportNoNamedExports_0"' declares 'a' locally, but it is not exported.
13+
!!! related TS2728 tests/cases/compiler/es6ImportNamedImportNoNamedExports_0.ts:1:5: 'a' is declared here.
1314
import { a as x } from "./es6ImportNamedImportNoNamedExports_0";
1415
~
15-
!!! error TS2305: Module '"./es6ImportNamedImportNoNamedExports_0"' has no exported member 'a'.
16+
!!! error TS2459: Module '"./es6ImportNamedImportNoNamedExports_0"' declares 'a' locally, but it is not exported.
17+
!!! related TS2728 tests/cases/compiler/es6ImportNamedImportNoNamedExports_0.ts:1:5: 'a' is declared here.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/compiler/b.ts(1,15): error TS2460: Module '"./a"' declares 'bar' locally, but it is exported as 'baz'.
2+
3+
4+
==== tests/cases/compiler/a.ts (0 errors) ====
5+
declare function foo(): any
6+
declare function bar(): any;
7+
export { foo, bar as baz };
8+
9+
==== tests/cases/compiler/b.ts (1 errors) ====
10+
import { foo, bar } from "./a";
11+
~~~
12+
!!! error TS2460: Module '"./a"' declares 'bar' locally, but it is exported as 'baz'.
13+
!!! related TS2728 tests/cases/compiler/a.ts:2:18: 'bar' is declared here.
14+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/importNonExportedMember.ts] ////
2+
3+
//// [a.ts]
4+
declare function foo(): any
5+
declare function bar(): any;
6+
export { foo, bar as baz };
7+
8+
//// [b.ts]
9+
import { foo, bar } from "./a";
10+
11+
12+
//// [a.js]
13+
"use strict";
14+
exports.__esModule = true;
15+
//// [b.js]
16+
"use strict";
17+
exports.__esModule = true;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/compiler/a.ts ===
2+
declare function foo(): any
3+
>foo : Symbol(foo, Decl(a.ts, 0, 0))
4+
5+
declare function bar(): any;
6+
>bar : Symbol(bar, Decl(a.ts, 0, 27))
7+
8+
export { foo, bar as baz };
9+
>foo : Symbol(foo, Decl(a.ts, 2, 8))
10+
>bar : Symbol(bar, Decl(a.ts, 0, 27))
11+
>baz : Symbol(baz, Decl(a.ts, 2, 13))
12+
13+
=== tests/cases/compiler/b.ts ===
14+
import { foo, bar } from "./a";
15+
>foo : Symbol(foo, Decl(b.ts, 0, 8))
16+
>bar : Symbol(bar, Decl(b.ts, 0, 13))
17+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/compiler/a.ts ===
2+
declare function foo(): any
3+
>foo : () => any
4+
5+
declare function bar(): any;
6+
>bar : () => any
7+
8+
export { foo, bar as baz };
9+
>foo : () => any
10+
>bar : () => any
11+
>baz : () => any
12+
13+
=== tests/cases/compiler/b.ts ===
14+
import { foo, bar } from "./a";
15+
>foo : () => any
16+
>bar : any
17+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/compiler/b.ts(1,10): error TS2459: Module '"./a"' declares 'bar' locally, but it is not exported.
2+
3+
4+
==== tests/cases/compiler/a.ts (0 errors) ====
5+
declare function foo(): any
6+
declare function bar(): any;
7+
export { foo };
8+
9+
==== tests/cases/compiler/b.ts (1 errors) ====
10+
import { bar } from "./a";
11+
~~~
12+
!!! error TS2459: Module '"./a"' declares 'bar' locally, but it is not exported.
13+
!!! related TS2728 tests/cases/compiler/a.ts:2:18: 'bar' is declared here.
14+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/importNonExportedMember1.ts] ////
2+
3+
//// [a.ts]
4+
declare function foo(): any
5+
declare function bar(): any;
6+
export { foo };
7+
8+
//// [b.ts]
9+
import { bar } from "./a";
10+
11+
12+
//// [a.js]
13+
"use strict";
14+
exports.__esModule = true;
15+
//// [b.js]
16+
"use strict";
17+
exports.__esModule = true;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/compiler/a.ts ===
2+
declare function foo(): any
3+
>foo : Symbol(foo, Decl(a.ts, 0, 0))
4+
5+
declare function bar(): any;
6+
>bar : Symbol(bar, Decl(a.ts, 0, 27))
7+
8+
export { foo };
9+
>foo : Symbol(foo, Decl(a.ts, 2, 8))
10+
11+
=== tests/cases/compiler/b.ts ===
12+
import { bar } from "./a";
13+
>bar : Symbol(bar, Decl(b.ts, 0, 8))
14+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/compiler/a.ts ===
2+
declare function foo(): any
3+
>foo : () => any
4+
5+
declare function bar(): any;
6+
>bar : () => any
7+
8+
export { foo };
9+
>foo : () => any
10+
11+
=== tests/cases/compiler/b.ts ===
12+
import { bar } from "./a";
13+
>bar : any
14+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @filename: a.ts
2+
declare function foo(): any
3+
declare function bar(): any;
4+
export { foo, bar as baz };
5+
6+
// @filename: b.ts
7+
import { foo, bar } from "./a";
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @filename: a.ts
2+
declare function foo(): any
3+
declare function bar(): any;
4+
export { foo };
5+
6+
// @filename: b.ts
7+
import { bar } from "./a";

0 commit comments

Comments
 (0)