Fix export * that resolves to something type-only (#36558)

* Fix `export *` that resolves to something type-only

* Add same tests but non-tsserver
This commit is contained in:
Andrew Branch 2020-01-31 15:09:27 -08:00 committed by GitHub
parent 8db1d7bc6d
commit 86556d6c03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 412 additions and 13 deletions

View File

@ -2372,7 +2372,10 @@ namespace ts {
function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined {
const moduleSpecifier = node.parent.moduleSpecifier;
return moduleSpecifier && resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
const immediate = moduleSpecifier && resolveExternalModuleName(node, moduleSpecifier);
const resolved = moduleSpecifier && resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false);
return resolved;
}
// This function creates a synthetic symbol that combines the value side of one symbol with the
@ -2521,7 +2524,9 @@ namespace ts {
}
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
const resolved = resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false);
return resolved;
}
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {

View File

@ -14,7 +14,7 @@ namespace ts.projectSystem {
content: "import { a } from './b'; new a.A();"
};
assertUsageError([a, b, c], c);
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
});
it("named export -> type-only named import -> named export -> named import", () => {
@ -31,7 +31,7 @@ namespace ts.projectSystem {
content: "import { A } from './b'; new A();"
};
assertUsageError([a, b, c], c);
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
});
it("named export -> type-only namespace import -> export equals -> import equals", () => {
@ -48,7 +48,7 @@ namespace ts.projectSystem {
content: "import a = require('./b'); new a.A();"
};
assertUsageError([a, b, c], c);
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
});
it("named export -> type-only namespace import -> export default -> import default", () => {
@ -65,13 +65,13 @@ namespace ts.projectSystem {
content: "import a from './b'; new a.A();"
};
assertUsageError([a, b, c], c);
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
});
it("export default -> type-only import default -> export default -> import default", () => {
const a = {
path: "/a.ts",
content: "export defai;t class A {}"
content: "export default class A {}"
};
const b = {
path: "/b.ts",
@ -82,11 +82,74 @@ namespace ts.projectSystem {
content: "import A from './b'; new A();"
};
assertUsageError([a, b, c], c);
assertUsageError([a, b, c], c, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
});
it("named export -> type-only export from -> export star from -> named import", () => {
const a = {
path: "/a.ts",
content: "export class A {}"
};
const b = {
path: "/b.ts",
content: "export type { A } from './a';"
};
const c = {
path: "/c.ts",
content: "export * from './b';"
};
const d = {
path: "/d.ts",
content: "import { A } from './c'; new A();"
};
assertUsageError([a, b, c, d], d, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type);
});
it("named export -> export namespace from -> type-only named import -> named export -> named import", () => {
const a = {
path: "/a.ts",
content: "export class A {}"
};
const b = {
path: "/b.ts",
content: "export * as a from './a';"
};
const c = {
path: "/c.ts",
content: "import type { a } from './b'; export { a };"
};
const d = {
path: "/d.ts",
content: "import { a } from './c'; new a.A();"
};
assertUsageError([a, b, c, d], d, Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type);
});
it("named export -> type-only export from -> export namespace from -> named import", () => {
const a = {
path: "/a.ts",
content: "export class A {}"
};
const b = {
path: "/b.ts",
content: "export type { A } from './a';"
};
const c = {
path: "/c.ts",
content: "export * as a from './b';"
};
const d = {
path: "/d.ts",
content: "import { a } from './c'; new a.A();"
};
assertUsageError([a, b, c, d], d, Diagnostics.Property_0_does_not_exist_on_type_1);
});
});
function assertUsageError(files: readonly TestFSWithWatch.File[], openFile: TestFSWithWatch.File) {
function assertUsageError(files: readonly TestFSWithWatch.File[], openFile: TestFSWithWatch.File, diagnostic: DiagnosticMessage) {
const host = createServerHost(files);
const session = createSession(host);
openFilesForSession([openFile], session);
@ -96,9 +159,6 @@ namespace ts.projectSystem {
);
const diagnostics = session.executeCommand(req).response as protocol.Diagnostic[];
assert.lengthOf(diagnostics, 1);
assert.oneOf(diagnostics[0].code, [
Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type.code,
Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type.code
]);
assert.equal(diagnostics[0].code, diagnostic.code);
}
}

View File

@ -0,0 +1,19 @@
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'.
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
export class A {}
==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
export type { A } from './a';
==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
export * from './b';
==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
import { A } from './c';
new A(); // Error
~
!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
!!! related TS1377 tests/cases/conformance/externalModules/typeOnly/b.ts:1:15: 'A' was exported here.

View File

@ -0,0 +1,39 @@
//// [tests/cases/conformance/externalModules/typeOnly/exportNamespace1.ts] ////
//// [a.ts]
export class A {}
//// [b.ts]
export type { A } from './a';
//// [c.ts]
export * from './b';
//// [d.ts]
import { A } from './c';
new A(); // Error
//// [a.js]
"use strict";
exports.__esModule = true;
var A = /** @class */ (function () {
function A() {
}
return A;
}());
exports.A = A;
//// [b.js]
"use strict";
exports.__esModule = true;
//// [c.js]
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
exports.__esModule = true;
__export(require("./b"));
//// [d.js]
"use strict";
exports.__esModule = true;
new A(); // Error

View File

@ -0,0 +1,18 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export class A {}
>A : Symbol(A, Decl(a.ts, 0, 0))
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export type { A } from './a';
>A : Symbol(A, Decl(b.ts, 0, 13))
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
export * from './b';
No type information for this code.
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { A } from './c';
>A : Symbol(A, Decl(d.ts, 0, 8))
new A(); // Error
>A : Symbol(A, Decl(d.ts, 0, 8))

View File

@ -0,0 +1,19 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export class A {}
>A : A
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export type { A } from './a';
>A : import("tests/cases/conformance/externalModules/typeOnly/a").A
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
export * from './b';
No type information for this code.
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { A } from './c';
>A : typeof A
new A(); // Error
>new A() : A
>A : typeof A

View File

@ -0,0 +1,20 @@
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'.
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
export class A {}
==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
export * as a from './a';
==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
import type { a } from './b';
export { a };
==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
import { a } from './c';
new a.A(); // Error
~
!!! error TS1361: 'a' cannot be used as a value because it was imported using 'import type'.
!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/c.ts:1:15: 'a' was imported here.

View File

@ -0,0 +1,37 @@
//// [tests/cases/conformance/externalModules/typeOnly/exportNamespace2.ts] ////
//// [a.ts]
export class A {}
//// [b.ts]
export * as a from './a';
//// [c.ts]
import type { a } from './b';
export { a };
//// [d.ts]
import { a } from './c';
new a.A(); // Error
//// [a.js]
"use strict";
exports.__esModule = true;
var A = /** @class */ (function () {
function A() {
}
return A;
}());
exports.A = A;
//// [b.js]
"use strict";
exports.__esModule = true;
exports.a = require("./a");
//// [c.js]
"use strict";
exports.__esModule = true;
//// [d.js]
"use strict";
exports.__esModule = true;
new a.A(); // Error

View File

@ -0,0 +1,24 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export class A {}
>A : Symbol(A, Decl(a.ts, 0, 0))
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export * as a from './a';
>a : Symbol(a, Decl(b.ts, 0, 11))
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
import type { a } from './b';
>a : Symbol(a, Decl(c.ts, 0, 13))
export { a };
>a : Symbol(a, Decl(c.ts, 1, 8))
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { a } from './c';
>a : Symbol(a, Decl(d.ts, 0, 8))
new a.A(); // Error
>a.A : Symbol(a.A, Decl(a.ts, 0, 0))
>a : Symbol(a, Decl(d.ts, 0, 8))
>A : Symbol(a.A, Decl(a.ts, 0, 0))

View File

@ -0,0 +1,25 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export class A {}
>A : A
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export * as a from './a';
>a : typeof a
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
import type { a } from './b';
>a : any
export { a };
>a : typeof a
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { a } from './c';
>a : typeof a
new a.A(); // Error
>new a.A() : a.A
>a.A : typeof a.A
>a : typeof a
>A : typeof a.A

View File

@ -0,0 +1,18 @@
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")'.
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
export class A {}
==== tests/cases/conformance/externalModules/typeOnly/b.ts (0 errors) ====
export type { A } from './a';
==== tests/cases/conformance/externalModules/typeOnly/c.ts (0 errors) ====
export * as a from './b';
==== tests/cases/conformance/externalModules/typeOnly/d.ts (1 errors) ====
import { a } from './c';
new a.A(); // Error
~
!!! error TS2339: Property 'A' does not exist on type 'typeof import("tests/cases/conformance/externalModules/typeOnly/b")'.

View File

@ -0,0 +1,37 @@
//// [tests/cases/conformance/externalModules/typeOnly/exportNamespace3.ts] ////
//// [a.ts]
export class A {}
//// [b.ts]
export type { A } from './a';
//// [c.ts]
export * as a from './b';
//// [d.ts]
import { a } from './c';
new a.A(); // Error
//// [a.js]
"use strict";
exports.__esModule = true;
var A = /** @class */ (function () {
function A() {
}
return A;
}());
exports.A = A;
//// [b.js]
"use strict";
exports.__esModule = true;
//// [c.js]
"use strict";
exports.__esModule = true;
exports.a = require("./b");
//// [d.js]
"use strict";
exports.__esModule = true;
var c_1 = require("./c");
new c_1.a.A(); // Error

View File

@ -0,0 +1,19 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export class A {}
>A : Symbol(A, Decl(a.ts, 0, 0))
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export type { A } from './a';
>A : Symbol(A, Decl(b.ts, 0, 13))
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
export * as a from './b';
>a : Symbol(a, Decl(c.ts, 0, 11))
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { a } from './c';
>a : Symbol(a, Decl(d.ts, 0, 8))
new a.A(); // Error
>a : Symbol(a, Decl(d.ts, 0, 8))

View File

@ -0,0 +1,22 @@
=== tests/cases/conformance/externalModules/typeOnly/a.ts ===
export class A {}
>A : A
=== tests/cases/conformance/externalModules/typeOnly/b.ts ===
export type { A } from './a';
>A : import("tests/cases/conformance/externalModules/typeOnly/a").A
=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
export * as a from './b';
>a : typeof a
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
import { a } from './c';
>a : typeof a
new a.A(); // Error
>new a.A() : any
>a.A : any
>a : typeof a
>A : any

View File

@ -0,0 +1,12 @@
// @Filename: a.ts
export class A {}
// @Filename: b.ts
export type { A } from './a';
// @Filename: c.ts
export * from './b';
// @Filename: d.ts
import { A } from './c';
new A(); // Error

View File

@ -0,0 +1,13 @@
// @Filename: a.ts
export class A {}
// @Filename: b.ts
export * as a from './a';
// @Filename: c.ts
import type { a } from './b';
export { a };
// @Filename: d.ts
import { a } from './c';
new a.A(); // Error

View File

@ -0,0 +1,12 @@
// @Filename: a.ts
export class A {}
// @Filename: b.ts
export type { A } from './a';
// @Filename: c.ts
export * as a from './b';
// @Filename: d.ts
import { a } from './c';
new a.A(); // Error