Skip to content

Commit 27bbfdb

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 ad322a5 commit 27bbfdb

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
@@ -2253,7 +2253,7 @@ namespace ts {
22532253
return links.target;
22542254
}
22552255

2256-
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
2256+
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) {
22572257
const symbol = getSymbolOfNode(node);
22582258
const target = resolveAlias(symbol);
22592259
if (target) {
@@ -2275,15 +2275,10 @@ namespace ts {
22752275
links.referenced = true;
22762276
const node = getDeclarationOfAliasSymbol(symbol);
22772277
if (!node) return Debug.fail();
2278-
if (node.kind === SyntaxKind.ExportAssignment) {
2279-
// export default <symbol>
2280-
checkExpressionCached((<ExportAssignment>node).expression);
2281-
}
2282-
else if (node.kind === SyntaxKind.ExportSpecifier) {
2283-
// export { <symbol> } or export { <symbol> as foo }
2284-
checkExpressionCached((<ExportSpecifier>node).propertyName || (<ExportSpecifier>node).name);
2285-
}
2286-
else if (isInternalModuleImportEqualsDeclaration(node)) {
2278+
// We defer checking of the reference of an `import =` until the import itself is referenced,
2279+
// This way a chain of imports can be elided if ultimately the final input is only used in a type
2280+
// position.
2281+
if (isInternalModuleImportEqualsDeclaration(node)) {
22872282
// import foo = <symbol>
22882283
checkExpressionCached(<Expression>node.moduleReference);
22892284
}
@@ -17430,7 +17425,7 @@ namespace ts {
1743017425
}
1743117426

1743217427
function markAliasReferenced(symbol: Symbol, location: Node) {
17433-
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
17428+
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) {
1743417429
markAliasSymbolAsReferenced(symbol);
1743517430
}
1743617431
}
@@ -19941,8 +19936,8 @@ namespace ts {
1994119936
// if jsx emit was not react as there wont be error being emitted
1994219937
reactSym.isReferenced = SymbolFlags.All;
1994319938

19944-
// If react symbol is alias, mark it as referenced
19945-
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
19939+
// If react symbol is alias, mark it as refereced
19940+
if (reactSym.flags & SymbolFlags.Alias) {
1994619941
markAliasSymbolAsReferenced(reactSym);
1994719942
}
1994819943
}
@@ -24163,7 +24158,7 @@ namespace ts {
2416324158
return result;
2416424159
}
2416524160

24166-
function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type {
24161+
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
2416724162
const links = getNodeLinks(node);
2416824163
if (!links.resolvedType) {
2416924164
if (checkMode && checkMode !== CheckMode.Normal) {
@@ -24471,7 +24466,8 @@ namespace ts {
2447124466
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
2447224467
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
2447324468
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node) ||
24474-
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node));
24469+
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node)) ||
24470+
(node.parent.kind === SyntaxKind.ExportSpecifier); // We allow reexporting const enums
2447524471

2447624472
if (!ok) {
2447724473
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);
@@ -29049,6 +29045,10 @@ namespace ts {
2904929045
}
2905029046
else {
2905129047
markExportAsReferenced(node);
29048+
const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
29049+
if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) {
29050+
checkExpressionCached(node.propertyName || node.name);
29051+
}
2905229052
}
2905329053
}
2905429054
}
@@ -29075,7 +29075,17 @@ namespace ts {
2907529075
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
2907629076
}
2907729077
if (node.expression.kind === SyntaxKind.Identifier) {
29078-
markExportAsReferenced(node);
29078+
const id = node.expression as Identifier;
29079+
const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node);
29080+
if (sym) {
29081+
markAliasReferenced(sym, id);
29082+
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
29083+
const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym;
29084+
if (target === unknownSymbol || target.flags & SymbolFlags.Value) {
29085+
// However if it is a value, we need to check it's being used correctly
29086+
checkExpressionCached(node.expression);
29087+
}
29088+
}
2907929089

2908029090
if (getEmitDeclarations(compilerOptions)) {
2908129091
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)