Skip to content

Commit 9a9a2df

Browse files
committed
Preserve const enums should keep import refs
for exported const enums exported via export default Move some functionality around, small cleanup Remove unneeded const enum check
1 parent 9d404b4 commit 9a9a2df

File tree

6 files changed

+116
-20
lines changed

6 files changed

+116
-20
lines changed

src/compiler/checker.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,7 +2361,7 @@ namespace ts {
23612361
return links.target;
23622362
}
23632363

2364-
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
2364+
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) {
23652365
const symbol = getSymbolOfNode(node);
23662366
const target = resolveAlias(symbol);
23672367
if (target) {
@@ -2383,15 +2383,10 @@ namespace ts {
23832383
links.referenced = true;
23842384
const node = getDeclarationOfAliasSymbol(symbol);
23852385
if (!node) return Debug.fail();
2386-
if (node.kind === SyntaxKind.ExportAssignment) {
2387-
// export default <symbol>
2388-
checkExpressionCached((<ExportAssignment>node).expression);
2389-
}
2390-
else if (node.kind === SyntaxKind.ExportSpecifier) {
2391-
// export { <symbol> } or export { <symbol> as foo }
2392-
checkExpressionCached((<ExportSpecifier>node).propertyName || (<ExportSpecifier>node).name);
2393-
}
2394-
else if (isInternalModuleImportEqualsDeclaration(node)) {
2386+
// We defer checking of the reference of an `import =` until the import itself is referenced,
2387+
// This way a chain of imports can be elided if ultimately the final input is only used in a type
2388+
// position.
2389+
if (isInternalModuleImportEqualsDeclaration(node)) {
23952390
// import foo = <symbol>
23962391
checkExpressionCached(<Expression>node.moduleReference);
23972392
}
@@ -17815,7 +17810,7 @@ namespace ts {
1781517810
}
1781617811

1781717812
function markAliasReferenced(symbol: Symbol, location: Node) {
17818-
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
17813+
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) {
1781917814
markAliasSymbolAsReferenced(symbol);
1782017815
}
1782117816
}
@@ -20326,8 +20321,8 @@ namespace ts {
2032620321
// if jsx emit was not react as there wont be error being emitted
2032720322
reactSym.isReferenced = SymbolFlags.All;
2032820323

20329-
// If react symbol is alias, mark it as referenced
20330-
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
20324+
// If react symbol is alias, mark it as refereced
20325+
if (reactSym.flags & SymbolFlags.Alias) {
2033120326
markAliasSymbolAsReferenced(reactSym);
2033220327
}
2033320328
}
@@ -24878,7 +24873,7 @@ namespace ts {
2487824873
return result;
2487924874
}
2488024875

24881-
function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type {
24876+
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
2488224877
const links = getNodeLinks(node);
2488324878
if (!links.resolvedType) {
2488424879
if (checkMode && checkMode !== CheckMode.Normal) {
@@ -25206,7 +25201,8 @@ namespace ts {
2520625201
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
2520725202
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
2520825203
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node) ||
25209-
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node));
25204+
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node)) ||
25205+
(node.parent.kind === SyntaxKind.ExportSpecifier); // We allow reexporting const enums
2521025206

2521125207
if (!ok) {
2521225208
error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query);
@@ -30145,6 +30141,10 @@ namespace ts {
3014530141
}
3014630142
else {
3014730143
markExportAsReferenced(node);
30144+
const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
30145+
if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) {
30146+
checkExpressionCached(node.propertyName || node.name);
30147+
}
3014830148
}
3014930149
}
3015030150
}
@@ -30171,7 +30171,17 @@ namespace ts {
3017130171
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
3017230172
}
3017330173
if (node.expression.kind === SyntaxKind.Identifier) {
30174-
markExportAsReferenced(node);
30174+
const id = node.expression as Identifier;
30175+
const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node);
30176+
if (sym) {
30177+
markAliasReferenced(sym, id);
30178+
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
30179+
const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym;
30180+
if (target === unknownSymbol || target.flags & SymbolFlags.Value) {
30181+
// However if it is a value, we need to check it's being used correctly
30182+
checkExpressionCached(node.expression);
30183+
}
30184+
}
3017530185

3017630186
if (getEmitDeclarations(compilerOptions)) {
3017730187
collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [tests/cases/compiler/constEnumPreserveEmitReexport.ts] ////
2+
3+
//// [ConstEnum.ts]
4+
export const enum MyConstEnum {
5+
Foo,
6+
Bar
7+
};
8+
//// [ImportExport.ts]
9+
import { MyConstEnum } from './ConstEnum';
10+
export default MyConstEnum;
11+
//// [ReExport.ts]
12+
export { MyConstEnum as default } from './ConstEnum';
13+
14+
//// [ConstEnum.js]
15+
"use strict";
16+
exports.__esModule = true;
17+
var MyConstEnum;
18+
(function (MyConstEnum) {
19+
MyConstEnum[MyConstEnum["Foo"] = 0] = "Foo";
20+
MyConstEnum[MyConstEnum["Bar"] = 1] = "Bar";
21+
})(MyConstEnum = exports.MyConstEnum || (exports.MyConstEnum = {}));
22+
;
23+
//// [ImportExport.js]
24+
"use strict";
25+
exports.__esModule = true;
26+
var ConstEnum_1 = require("./ConstEnum");
27+
exports["default"] = ConstEnum_1.MyConstEnum;
28+
//// [ReExport.js]
29+
"use strict";
30+
exports.__esModule = true;
31+
var ConstEnum_1 = require("./ConstEnum");
32+
exports["default"] = ConstEnum_1.MyConstEnum;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/ConstEnum.ts ===
2+
export const enum MyConstEnum {
3+
>MyConstEnum : Symbol(MyConstEnum, Decl(ConstEnum.ts, 0, 0))
4+
5+
Foo,
6+
>Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.ts, 0, 31))
7+
8+
Bar
9+
>Bar : Symbol(MyConstEnum.Bar, Decl(ConstEnum.ts, 1, 8))
10+
11+
};
12+
=== tests/cases/compiler/ImportExport.ts ===
13+
import { MyConstEnum } from './ConstEnum';
14+
>MyConstEnum : Symbol(MyConstEnum, Decl(ImportExport.ts, 0, 8))
15+
16+
export default MyConstEnum;
17+
>MyConstEnum : Symbol(MyConstEnum, Decl(ImportExport.ts, 0, 8))
18+
19+
=== tests/cases/compiler/ReExport.ts ===
20+
export { MyConstEnum as default } from './ConstEnum';
21+
>MyConstEnum : Symbol(MyConstEnum, Decl(ConstEnum.ts, 0, 0))
22+
>default : Symbol(default, Decl(ReExport.ts, 0, 8))
23+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/ConstEnum.ts ===
2+
export const enum MyConstEnum {
3+
>MyConstEnum : MyConstEnum
4+
5+
Foo,
6+
>Foo : MyConstEnum.Foo
7+
8+
Bar
9+
>Bar : MyConstEnum.Bar
10+
11+
};
12+
=== tests/cases/compiler/ImportExport.ts ===
13+
import { MyConstEnum } from './ConstEnum';
14+
>MyConstEnum : typeof MyConstEnum
15+
16+
export default MyConstEnum;
17+
>MyConstEnum : MyConstEnum
18+
19+
=== tests/cases/compiler/ReExport.ts ===
20+
export { MyConstEnum as default } from './ConstEnum';
21+
>MyConstEnum : typeof import("tests/cases/compiler/ConstEnum").MyConstEnum
22+
>default : typeof import("tests/cases/compiler/ConstEnum").MyConstEnum
23+

tests/baselines/reference/exportAsNamespaceConflict.errors.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
/a.d.ts(2,10): error TS2708: Cannot use namespace 'N' as a value.
21
/a.d.ts(3,1): error TS2303: Circular definition of import alias 'N'.
32

43

5-
==== /a.d.ts (2 errors) ====
4+
==== /a.d.ts (1 errors) ====
65
declare global { namespace N {} }
76
export = N;
8-
~
9-
!!! error TS2708: Cannot use namespace 'N' as a value.
107
export as namespace N;
118
~~~~~~~~~~~~~~~~~~~~~~
129
!!! error TS2303: Circular definition of import alias 'N'.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @preserveConstEnums: true
2+
// @filename: ConstEnum.ts
3+
export const enum MyConstEnum {
4+
Foo,
5+
Bar
6+
};
7+
// @filename: ImportExport.ts
8+
import { MyConstEnum } from './ConstEnum';
9+
export default MyConstEnum;
10+
// @filename: ReExport.ts
11+
export { MyConstEnum as default } from './ConstEnum';

0 commit comments

Comments
 (0)