Skip to content

Commit 0a6ee77

Browse files
authored
Grammar error on export type * (microsoft#37064)
* Recognize `export type *` syntax, but disallow it * Add more comments to test * Revert recognizing invalid forms as type-only * Revert more
1 parent f1457c1 commit 0a6ee77

File tree

8 files changed

+164
-4
lines changed

8 files changed

+164
-4
lines changed

src/compiler/checker.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -1936,10 +1936,11 @@ namespace ts {
19361936
if (!isValidTypeOnlyAliasUseSite(useSite)) {
19371937
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol);
19381938
if (typeOnlyDeclaration) {
1939-
const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
1939+
const isExport = typeOnlyDeclarationIsExport(typeOnlyDeclaration);
1940+
const message = isExport
19401941
? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type
19411942
: Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type;
1942-
const relatedMessage = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
1943+
const relatedMessage = isExport
19431944
? Diagnostics._0_was_exported_here
19441945
: Diagnostics._0_was_imported_here;
19451946
const unescapedName = unescapeLeadingUnderscores(name);
@@ -2286,12 +2287,14 @@ namespace ts {
22862287
function checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node: ImportEqualsDeclaration, resolved: Symbol | undefined) {
22872288
if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false)) {
22882289
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(getSymbolOfNode(node))!;
2289-
const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
2290+
const isExport = typeOnlyDeclarationIsExport(typeOnlyDeclaration);
2291+
const message = isExport
22902292
? Diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_exported_using_export_type
22912293
: Diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_imported_using_import_type;
2292-
const relatedMessage = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
2294+
const relatedMessage = isExport
22932295
? Diagnostics._0_was_exported_here
22942296
: Diagnostics._0_was_imported_here;
2297+
22952298
// Non-null assertion is safe because the optionality comes from ImportClause,
22962299
// but if an ImportClause was the typeOnlyDeclaration, it had to have a `name`.
22972300
const name = unescapeLeadingUnderscores(typeOnlyDeclaration.name!.escapedText);
@@ -33598,6 +33601,7 @@ namespace ts {
3359833601
checkExternalEmitHelpers(node, ExternalEmitHelpers.CreateBinding);
3359933602
}
3360033603

33604+
checkGrammarExportDeclaration(node);
3360133605
if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) {
3360233606
if (node.exportClause) {
3360333607
// export { x, y }
@@ -33630,6 +33634,14 @@ namespace ts {
3363033634
}
3363133635
}
3363233636

33637+
function checkGrammarExportDeclaration(node: ExportDeclaration): boolean {
33638+
const isTypeOnlyExportStar = node.isTypeOnly && node.exportClause?.kind !== SyntaxKind.NamedExports;
33639+
if (isTypeOnlyExportStar) {
33640+
grammarErrorOnNode(node, Diagnostics.Only_named_exports_may_use_export_type);
33641+
}
33642+
return !isTypeOnlyExportStar;
33643+
}
33644+
3363333645
function checkGrammarModuleElementContext(node: Statement, errorMessage: DiagnosticMessage): boolean {
3363433646
const isInAppropriateContext = node.parent.kind === SyntaxKind.SourceFile || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.ModuleDeclaration;
3363533647
if (!isInAppropriateContext) {

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,10 @@
11431143
"category": "Error",
11441144
"code": 1382
11451145
},
1146+
"Only named exports may use 'export type'.": {
1147+
"category": "Error",
1148+
"code": 1383
1149+
},
11461150

11471151
"The types of '{0}' are incompatible between these types.": {
11481152
"category": "Error",

src/compiler/utilities.ts

+4
Original file line numberDiff line numberDiff line change
@@ -6220,6 +6220,10 @@ namespace ts {
62206220
|| !isExpressionNode(useSite);
62216221
}
62226222

6223+
export function typeOnlyDeclarationIsExport(typeOnlyDeclaration: Node) {
6224+
return typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier;
6225+
}
6226+
62236227
function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node) {
62246228
while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) {
62256229
node = node.parent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tests/cases/conformance/externalModules/typeOnly/b.ts(1,1): error TS1383: Only named exports may use 'export type'.
2+
tests/cases/conformance/externalModules/typeOnly/c.ts(1,1): error TS1383: Only named exports may use 'export type'.
3+
4+
5+
==== tests/cases/conformance/externalModules/typeOnly/a.ts (0 errors) ====
6+
export class A {}
7+
8+
==== tests/cases/conformance/externalModules/typeOnly/b.ts (1 errors) ====
9+
export type * from './a'; // Grammar error
10+
~~~~~~~~~~~~~~~~~~~~~~~~~
11+
!!! error TS1383: Only named exports may use 'export type'.
12+
13+
==== tests/cases/conformance/externalModules/typeOnly/c.ts (1 errors) ====
14+
export type * as ns from './a'; // Grammar error
15+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16+
!!! error TS1383: Only named exports may use 'export type'.
17+
18+
==== tests/cases/conformance/externalModules/typeOnly/d.ts (0 errors) ====
19+
import { A } from './b';
20+
A;
21+
22+
==== tests/cases/conformance/externalModules/typeOnly/e.ts (0 errors) ====
23+
import { ns } from './c';
24+
ns.A;
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [tests/cases/conformance/externalModules/typeOnly/exportNamespace4.ts] ////
2+
3+
//// [a.ts]
4+
export class A {}
5+
6+
//// [b.ts]
7+
export type * from './a'; // Grammar error
8+
9+
//// [c.ts]
10+
export type * as ns from './a'; // Grammar error
11+
12+
//// [d.ts]
13+
import { A } from './b';
14+
A;
15+
16+
//// [e.ts]
17+
import { ns } from './c';
18+
ns.A;
19+
20+
21+
//// [a.js]
22+
"use strict";
23+
exports.__esModule = true;
24+
var A = /** @class */ (function () {
25+
function A() {
26+
}
27+
return A;
28+
}());
29+
exports.A = A;
30+
//// [b.js]
31+
"use strict";
32+
exports.__esModule = true;
33+
//// [c.js]
34+
"use strict";
35+
exports.__esModule = true;
36+
//// [d.js]
37+
"use strict";
38+
exports.__esModule = true;
39+
var b_1 = require("./b");
40+
b_1.A;
41+
//// [e.js]
42+
"use strict";
43+
exports.__esModule = true;
44+
var c_1 = require("./c");
45+
c_1.ns.A;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 * from './a'; // Grammar error
7+
No type information for this code.
8+
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
9+
export type * as ns from './a'; // Grammar error
10+
>ns : Symbol(ns, Decl(c.ts, 0, 11))
11+
12+
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
13+
import { A } from './b';
14+
>A : Symbol(A, Decl(d.ts, 0, 8))
15+
16+
A;
17+
>A : Symbol(A, Decl(d.ts, 0, 8))
18+
19+
=== tests/cases/conformance/externalModules/typeOnly/e.ts ===
20+
import { ns } from './c';
21+
>ns : Symbol(ns, Decl(e.ts, 0, 8))
22+
23+
ns.A;
24+
>ns.A : Symbol(ns.A, Decl(a.ts, 0, 0))
25+
>ns : Symbol(ns, Decl(e.ts, 0, 8))
26+
>A : Symbol(ns.A, Decl(a.ts, 0, 0))
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 * from './a'; // Grammar error
7+
No type information for this code.
8+
No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/c.ts ===
9+
export type * as ns from './a'; // Grammar error
10+
>ns : typeof ns
11+
12+
=== tests/cases/conformance/externalModules/typeOnly/d.ts ===
13+
import { A } from './b';
14+
>A : typeof A
15+
16+
A;
17+
>A : typeof A
18+
19+
=== tests/cases/conformance/externalModules/typeOnly/e.ts ===
20+
import { ns } from './c';
21+
>ns : typeof ns
22+
23+
ns.A;
24+
>ns.A : typeof ns.A
25+
>ns : typeof ns
26+
>A : typeof ns.A
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @Filename: a.ts
2+
export class A {}
3+
4+
// @Filename: b.ts
5+
export type * from './a'; // Grammar error
6+
7+
// @Filename: c.ts
8+
export type * as ns from './a'; // Grammar error
9+
10+
// @Filename: d.ts
11+
import { A } from './b';
12+
A;
13+
14+
// @Filename: e.ts
15+
import { ns } from './c';
16+
ns.A;

0 commit comments

Comments
 (0)