Skip to content

Error on export default of type: issue https://github.com/microsoft/TypeScript/issues/55087 #55097

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45846,12 +45846,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const id = node.expression as Identifier;
const sym = getExportSymbolOfValueSymbolIfExported(resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node));
if (sym) {
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(sym, SymbolFlags.Value);
markAliasReferenced(sym, id);
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
if (getSymbolFlags(sym) & SymbolFlags.Value) {
// However if it is a value, we need to check it's being used correctly
checkExpressionCached(id);
if (!isIllegalExportDefaultInCJS && !(node.flags & NodeFlags.Ambient) && compilerOptions.verbatimModuleSyntax && getTypeOnlyAliasDeclaration(sym, SymbolFlags.Value)) {
if (!isIllegalExportDefaultInCJS && !(node.flags & NodeFlags.Ambient) && compilerOptions.verbatimModuleSyntax && typeOnlyDeclaration) {
error(
id,
node.isExportEquals
Expand All @@ -45870,6 +45871,44 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
idText(id),
);
}

if (!isIllegalExportDefaultInCJS && getIsolatedModules(compilerOptions) && !(sym.flags & SymbolFlags.Value)) {
if (
sym.flags & SymbolFlags.Alias
&& resolveAlias(sym) !== unknownSymbol
&& getSymbolFlags(sym, /*excludeTypeOnlyMeanings*/ false, /*excludeLocalMeanings*/ true) & SymbolFlags.Type
&& (!typeOnlyDeclaration || getSourceFileOfNode(typeOnlyDeclaration) !== getSourceFileOfNode(node))
) {
// import { SomeType } from "./someModule";
// export default SomeType; OR
// export = SomeType;
error(
id,
node.isExportEquals ?
Diagnostics._0_resolves_to_a_type_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_import_type_where_0_is_imported
: Diagnostics._0_resolves_to_a_type_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_export_type_0_as_default,
idText(id),
isolatedModulesLikeFlagName,
);
}
else if (typeOnlyDeclaration && getSourceFileOfNode(typeOnlyDeclaration) !== getSourceFileOfNode(node)) {
// import { SomeTypeOnlyValue } from "./someModule";
// export default SomeTypeOnlyValue; OR
// export = SomeTypeOnlyValue;
addTypeOnlyDeclarationRelatedInfo(
error(
id,
node.isExportEquals ?
Diagnostics._0_resolves_to_a_type_only_declaration_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_import_type_where_0_is_imported
: Diagnostics._0_resolves_to_a_type_only_declaration_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_export_type_0_as_default,
idText(id),
isolatedModulesLikeFlagName,
),
typeOnlyDeclaration,
idText(id),
);
}
}
}
else {
checkExpressionCached(id); // doesn't resolve, check as expression to mark as error
Expand Down Expand Up @@ -47558,7 +47597,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
);
case SyntaxKind.ExportAssignment:
return (node as ExportAssignment).expression && (node as ExportAssignment).expression.kind === SyntaxKind.Identifier ?
isAliasResolvedToValue(getSymbolOfDeclaration(node as ExportAssignment)) :
isAliasResolvedToValue(getSymbolOfDeclaration(node as ExportAssignment), /*excludeTypeOnlyValues*/ true) :
true;
}
return false;
Expand Down
16 changes: 16 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,22 @@
"category": "Error",
"code": 1288
},
"'{0}' resolves to a type-only declaration and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'import type' where '{0}' is imported.": {
"category": "Error",
"code": 1289
},
"'{0}' resolves to a type-only declaration and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'export type { {0} as default }'.": {
"category": "Error",
"code": 1290
},
"'{0}' resolves to a type and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'import type' where '{0}' is imported.": {
"category": "Error",
"code": 1291
},
"'{0}' resolves to a type and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'export type { {0} as default }'.": {
"category": "Error",
"code": 1292
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@
~
!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
!!! related TS1377 /a.ts:2:15: 'A' was exported here.


==== /c.ts (0 errors) ====
import type { A } from './a';
export = A;

==== /d.ts (0 errors) ====
import { A } from './a';
export = A;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import { A } from './a';
declare const a: A;
new A();

//// [c.ts]
import type { A } from './a';
export = A;

//// [d.ts]
import { A } from './a';
export = A;

//// [a.js]
"use strict";
Expand All @@ -22,3 +29,9 @@ var A = /** @class */ (function () {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
new A();
//// [c.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//// [d.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,17 @@ declare const a: A;
new A();
>A : Symbol(A, Decl(b.ts, 0, 8))

=== /c.ts ===
import type { A } from './a';
>A : Symbol(A, Decl(c.ts, 0, 13))

export = A;
>A : Symbol(A, Decl(c.ts, 0, 13))

=== /d.ts ===
import { A } from './a';
>A : Symbol(A, Decl(d.ts, 0, 8))

export = A;
>A : Symbol(A, Decl(d.ts, 0, 8))

Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,17 @@ new A();
>new A() : A
>A : typeof A

=== /c.ts ===
import type { A } from './a';
>A : A

export = A;
>A : A

=== /d.ts ===
import { A } from './a';
>A : typeof A

export = A;
>A : A

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/b.ts(3,5): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
/d.ts(2,10): error TS1291: 'A' resolves to a type and must be marked type-only in this file before re-exporting when 'isolatedModules' is enabled. Consider using 'import type' where 'A' is imported.


==== /a.ts (0 errors) ====
class A {}
export type { A };

==== /b.ts (1 errors) ====
import { A } from './a';
declare const a: A;
new A();
~
!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
!!! related TS1377 /a.ts:2:15: 'A' was exported here.

==== /c.ts (0 errors) ====
import type { A } from './a';
export = A;

==== /d.ts (1 errors) ====
import { A } from './a';
export = A;
~
!!! error TS1291: 'A' resolves to a type and must be marked type-only in this file before re-exporting when 'isolatedModules' is enabled. Consider using 'import type' where 'A' is imported.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts] ////

//// [a.ts]
class A {}
export type { A };

//// [b.ts]
import { A } from './a';
declare const a: A;
new A();

//// [c.ts]
import type { A } from './a';
export = A;

//// [d.ts]
import { A } from './a';
export = A;

//// [a.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var A = /** @class */ (function () {
function A() {
}
return A;
}());
//// [b.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
new A();
//// [c.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//// [d.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts] ////

=== /a.ts ===
class A {}
>A : Symbol(A, Decl(a.ts, 0, 0))

export type { A };
>A : Symbol(A, Decl(a.ts, 1, 13))

=== /b.ts ===
import { A } from './a';
>A : Symbol(A, Decl(b.ts, 0, 8))

declare const a: A;
>a : Symbol(a, Decl(b.ts, 1, 13))
>A : Symbol(A, Decl(b.ts, 0, 8))

new A();
>A : Symbol(A, Decl(b.ts, 0, 8))

=== /c.ts ===
import type { A } from './a';
>A : Symbol(A, Decl(c.ts, 0, 13))

export = A;
>A : Symbol(A, Decl(c.ts, 0, 13))

=== /d.ts ===
import { A } from './a';
>A : Symbol(A, Decl(d.ts, 0, 8))

export = A;
>A : Symbol(A, Decl(d.ts, 0, 8))

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts] ////

=== /a.ts ===
class A {}
>A : A

export type { A };
>A : A

=== /b.ts ===
import { A } from './a';
>A : typeof A

declare const a: A;
>a : A

new A();
>new A() : A
>A : typeof A

=== /c.ts ===
import type { A } from './a';
>A : A

export = A;
>A : A

=== /d.ts ===
import { A } from './a';
>A : typeof A

export = A;
>A : A

1 change: 0 additions & 1 deletion tests/baselines/reference/exportDefault.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ exports.A = A;
//// [b.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = types;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeppp

//// [c.js]
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/importEquals1.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var A = /** @class */ (function () {
exports.A = A;
//// [b.js]
"use strict";
module.exports = types;
Object.defineProperty(exports, "__esModule", { value: true });
//// [c.js]
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/test1.ts(1,10): error TS2865: Import 'T' conflicts with local value, so must be declared with a type-only import when 'isolatedModules' is enabled.
/test2.ts(1,10): error TS2440: Import declaration conflicts with local declaration of 'T'.
/test2.ts(3,16): error TS1292: 'T' resolves to a type and must be marked type-only in this file before re-exporting when 'isolatedModules' is enabled. Consider using 'export type { T as default }'.
/test3.ts(2,16): error TS1292: 'T' resolves to a type and must be marked type-only in this file before re-exporting when 'isolatedModules' is enabled. Consider using 'export type { T as default }'.


==== /type.ts (0 errors) ====
export type T = number;

==== /test1.ts (1 errors) ====
import { T } from "./type";
~
!!! error TS2865: Import 'T' conflicts with local value, so must be declared with a type-only import when 'isolatedModules' is enabled.
const T = 0; // Error as of #56354
export default T; // Ok

==== /test2.ts (2 errors) ====
import { T } from "./type";
~
!!! error TS2440: Import declaration conflicts with local declaration of 'T'.
type T = number; // Merge error
export default T; // Transpiler could assume the alias resolves to a value?
~
!!! error TS1292: 'T' resolves to a type and must be marked type-only in this file before re-exporting when 'isolatedModules' is enabled. Consider using 'export type { T as default }'.

==== /test3.ts (1 errors) ====
import { T } from "./type";
export default T; // Error
~
!!! error TS1292: 'T' resolves to a type and must be marked type-only in this file before re-exporting when 'isolatedModules' is enabled. Consider using 'export type { T as default }'.

==== /test4.ts (0 errors) ====
// @ts-expect-error
import unresolved from "./doesntexist";
export default unresolved;

Loading