Skip to content

Commit 86556d6

Browse files
authored
Fix export * that resolves to something type-only (#36558)
* Fix `export *` that resolves to something type-only * Add same tests but non-tsserver
1 parent 8db1d7b commit 86556d6

17 files changed

+412
-13
lines changed

src/compiler/checker.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2372,7 +2372,10 @@ namespace ts {
23722372

23732373
function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined {
23742374
const moduleSpecifier = node.parent.moduleSpecifier;
2375-
return moduleSpecifier && resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
2375+
const immediate = moduleSpecifier && resolveExternalModuleName(node, moduleSpecifier);
2376+
const resolved = moduleSpecifier && resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
2377+
markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false);
2378+
return resolved;
23762379
}
23772380

23782381
// This function creates a synthetic symbol that combines the value side of one symbol with the
@@ -2521,7 +2524,9 @@ namespace ts {
25212524
}
25222525

25232526
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
2524-
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
2527+
const resolved = resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
2528+
markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false);
2529+
return resolved;
25252530
}
25262531

25272532
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {

src/testRunner/unittests/tsserver/typeOnlyImportChains.ts

+71-11
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace ts.projectSystem {
1414
content: "import { a } from './b'; new a.A();"
1515
};
1616

17-
assertUsageError([a, b, c], c);
17+
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
1818
});
1919

2020
it("named export -> type-only named import -> named export -> named import", () => {
@@ -31,7 +31,7 @@ namespace ts.projectSystem {
3131
content: "import { A } from './b'; new A();"
3232
};
3333

34-
assertUsageError([a, b, c], c);
34+
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
3535
});
3636

3737
it("named export -> type-only namespace import -> export equals -> import equals", () => {
@@ -48,7 +48,7 @@ namespace ts.projectSystem {
4848
content: "import a = require('./b'); new a.A();"
4949
};
5050

51-
assertUsageError([a, b, c], c);
51+
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
5252
});
5353

5454
it("named export -> type-only namespace import -> export default -> import default", () => {
@@ -65,13 +65,13 @@ namespace ts.projectSystem {
6565
content: "import a from './b'; new a.A();"
6666
};
6767

68-
assertUsageError([a, b, c], c);
68+
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
6969
});
7070

7171
it("export default -> type-only import default -> export default -> import default", () => {
7272
const a = {
7373
path: "/a.ts",
74-
content: "export defai;t class A {}"
74+
content: "export default class A {}"
7575
};
7676
const b = {
7777
path: "/b.ts",
@@ -82,11 +82,74 @@ namespace ts.projectSystem {
8282
content: "import A from './b'; new A();"
8383
};
8484

85-
assertUsageError([a, b, c], c);
85+
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
86+
});
87+
88+
it("named export -> type-only export from -> export star from -> named import", () => {
89+
const a = {
90+
path: "/a.ts",
91+
content: "export class A {}"
92+
};
93+
const b = {
94+
path: "/b.ts",
95+
content: "export type { A } from './a';"
96+
};
97+
const c = {
98+
path: "/c.ts",
99+
content: "export * from './b';"
100+
};
101+
const d = {
102+
path: "/d.ts",
103+
content: "import { A } from './c'; new A();"
104+
};
105+
106+
assertUsageError([a, b, c, d], d, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type);
107+
});
108+
109+
it("named export -> export namespace from -> type-only named import -> named export -> named import", () => {
110+
const a = {
111+
path: "/a.ts",
112+
content: "export class A {}"
113+
};
114+
const b = {
115+
path: "/b.ts",
116+
content: "export * as a from './a';"
117+
};
118+
const c = {
119+
path: "/c.ts",
120+
content: "import type { a } from './b'; export { a };"
121+
};
122+
const d = {
123+
path: "/d.ts",
124+
content: "import { a } from './c'; new a.A();"
125+
};
126+
127+
assertUsageError([a, b, c, d], d, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
128+
});
129+
130+
it("named export -> type-only export from -> export namespace from -> named import", () => {
131+
const a = {
132+
path: "/a.ts",
133+
content: "export class A {}"
134+
};
135+
const b = {
136+
path: "/b.ts",
137+
content: "export type { A } from './a';"
138+
};
139+
const c = {
140+
path: "/c.ts",
141+
content: "export * as a from './b';"
142+
};
143+
const d = {
144+
path: "/d.ts",
145+
content: "import { a } from './c'; new a.A();"
146+
};
147+
148+
assertUsageError([a, b, c, d], d, Diagnostics.Property_0_does_not_exist_on_type_1);
86149
});
87150
});
88151

89-
function assertUsageError(files: readonly TestFSWithWatch.File[], openFile: TestFSWithWatch.File) {
152+
function assertUsageError(files: readonly TestFSWithWatch.File[], openFile: TestFSWithWatch.File, diagnostic: DiagnosticMessage) {
90153
const host = createServerHost(files);
91154
const session = createSession(host);
92155
openFilesForSession([openFile], session);
@@ -96,9 +159,6 @@ namespace ts.projectSystem {
96159
);
97160
const diagnostics = session.executeCommand(req).response as protocol.Diagnostic[];
98161
assert.lengthOf(diagnostics, 1);
99-
assert.oneOf(diagnostics[0].code, [
100-
Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type.code,
101-
Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type.code
102-
]);
162+
assert.equal(diagnostics[0].code, diagnostic.code);
103163
}
104164
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/conformance/externalModules/typeOnly/d.ts(2,5): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
2+
3+
4+
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
5+
export class A {}
6+
7+
==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
8+
export type { A } from './a';
9+
10+
==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
11+
export * from './b';
12+
13+
==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
14+
import { A } from './c';
15+
new A(); // Error
16+
~
17+
!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
18+
!!! related TS1377 tests/cases/conformance/externalModules/typeOnly/b.ts:1:15: 'A' was exported here.
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [tests/cases/conformance/externalModules/typeOnly/exportNamespace1.ts] ////
2+
3+
//// [a.ts]
4+
export class A {}
5+
6+
//// [b.ts]
7+
export type { A } from './a';
8+
9+
//// [c.ts]
10+
export * from './b';
11+
12+
//// [d.ts]
13+
import { A } from './c';
14+
new A(); // Error
15+
16+
17+
//// [a.js]
18+
"use strict";
19+
exports.__esModule = true;
20+
var A = /** @class */ (function () {
21+
function A() {
22+
}
23+
return A;
24+
}());
25+
exports.A = A;
26+
//// [b.js]
27+
"use strict";
28+
exports.__esModule = true;
29+
//// [c.js]
30+
"use strict";
31+
function __export(m) {
32+
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
33+
}
34+
exports.__esModule = true;
35+
__export(require("./b"));
36+
//// [d.js]
37+
"use strict";
38+
exports.__esModule = true;
39+
new A(); // Error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
2+
export class A {}
3+
>A : Symbol(A, Decl(a.ts, 0, 0))
4+
5+
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
6+
export type { A } from './a';
7+
>A : Symbol(A, Decl(b.ts, 0, 13))
8+
9+
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
10+
export * from './b';
11+
No type information for this code.
12+
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
13+
import { A } from './c';
14+
>A : Symbol(A, Decl(d.ts, 0, 8))
15+
16+
new A(); // Error
17+
>A : Symbol(A, Decl(d.ts, 0, 8))
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
2+
export class A {}
3+
>A : A
4+
5+
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
6+
export type { A } from './a';
7+
>A : import("tests/cases/conformance/externalModules/typeOnly/a").A
8+
9+
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
10+
export * from './b';
11+
No type information for this code.
12+
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
13+
import { A } from './c';
14+
>A : typeof A
15+
16+
new A(); // Error
17+
>new A() : A
18+
>A : typeof A
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
tests/cases/conformance/externalModules/typeOnly/d.ts(2,5): error TS1361: 'a' cannot be used as a value because it was imported using 'import type'.
2+
3+
4+
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
5+
export class A {}
6+
7+
==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
8+
export * as a from './a';
9+
10+
==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
11+
import type { a } from './b';
12+
export { a };
13+
14+
==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
15+
import { a } from './c';
16+
new a.A(); // Error
17+
~
18+
!!! error TS1361: 'a' cannot be used as a value because it was imported using 'import type'.
19+
!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/c.ts:1:15: 'a' was imported here.
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/conformance/externalModules/typeOnly/exportNamespace2.ts] ////
2+
3+
//// [a.ts]
4+
export class A {}
5+
6+
//// [b.ts]
7+
export * as a from './a';
8+
9+
//// [c.ts]
10+
import type { a } from './b';
11+
export { a };
12+
13+
//// [d.ts]
14+
import { a } from './c';
15+
new a.A(); // Error
16+
17+
18+
//// [a.js]
19+
"use strict";
20+
exports.__esModule = true;
21+
var A = /** @class */ (function () {
22+
function A() {
23+
}
24+
return A;
25+
}());
26+
exports.A = A;
27+
//// [b.js]
28+
"use strict";
29+
exports.__esModule = true;
30+
exports.a = require("./a");
31+
//// [c.js]
32+
"use strict";
33+
exports.__esModule = true;
34+
//// [d.js]
35+
"use strict";
36+
exports.__esModule = true;
37+
new a.A(); // Error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
2+
export class A {}
3+
>A : Symbol(A, Decl(a.ts, 0, 0))
4+
5+
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
6+
export * as a from './a';
7+
>a : Symbol(a, Decl(b.ts, 0, 11))
8+
9+
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
10+
import type { a } from './b';
11+
>a : Symbol(a, Decl(c.ts, 0, 13))
12+
13+
export { a };
14+
>a : Symbol(a, Decl(c.ts, 1, 8))
15+
16+
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
17+
import { a } from './c';
18+
>a : Symbol(a, Decl(d.ts, 0, 8))
19+
20+
new a.A(); // Error
21+
>a.A : Symbol(a.A, Decl(a.ts, 0, 0))
22+
>a : Symbol(a, Decl(d.ts, 0, 8))
23+
>A : Symbol(a.A, Decl(a.ts, 0, 0))
24+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
2+
export class A {}
3+
>A : A
4+
5+
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
6+
export * as a from './a';
7+
>a : typeof a
8+
9+
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
10+
import type { a } from './b';
11+
>a : any
12+
13+
export { a };
14+
>a : typeof a
15+
16+
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
17+
import { a } from './c';
18+
>a : typeof a
19+
20+
new a.A(); // Error
21+
>new a.A() : a.A
22+
>a.A : typeof a.A
23+
>a : typeof a
24+
>A : typeof a.A
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/conformance/externalModules/typeOnly/d.ts(2,7): error TS2339: Property 'A' does not exist on type 'typeof import("tests/cases/conformance/externalModules/typeOnly/b")'.
2+
3+
4+
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
5+
export class A {}
6+
7+
==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
8+
export type { A } from './a';
9+
10+
==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
11+
export * as a from './b';
12+
13+
==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
14+
import { a } from './c';
15+
new a.A(); // Error
16+
~
17+
!!! error TS2339: Property 'A' does not exist on type 'typeof import("tests/cases/conformance/externalModules/typeOnly/b")'.
18+

0 commit comments

Comments
 (0)