Skip to content

Commit 83893b3

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 a06ab85 commit 83893b3

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
}
@@ -17352,7 +17347,7 @@ namespace ts {
1735217347
}
1735317348

1735417349
function markAliasReferenced(symbol: Symbol, location: Node) {
17355-
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
17350+
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) {
1735617351
markAliasSymbolAsReferenced(symbol);
1735717352
}
1735817353
}
@@ -19848,8 +19843,8 @@ namespace ts {
1984819843
// if jsx emit was not react as there wont be error being emitted
1984919844
reactSym.isReferenced = SymbolFlags.All;
1985019845

19851-
// If react symbol is alias, mark it as referenced
19852-
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
19846+
// If react symbol is alias, mark it as refereced
19847+
if (reactSym.flags & SymbolFlags.Alias) {
1985319848
markAliasSymbolAsReferenced(reactSym);
1985419849
}
1985519850
}
@@ -24048,7 +24043,7 @@ namespace ts {
2404824043
return result;
2404924044
}
2405024045

24051-
function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type {
24046+
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
2405224047
const links = getNodeLinks(node);
2405324048
if (!links.resolvedType) {
2405424049
if (checkMode && checkMode !== CheckMode.Normal) {
@@ -24356,7 +24351,8 @@ namespace ts {
2435624351
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
2435724352
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
2435824353
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node) ||
24359-
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node));
24354+
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node)) ||
24355+
(node.parent.kind === SyntaxKind.ExportSpecifier); // We allow reexporting const enums
2436024356

2436124357
if (!ok) {
2436224358
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);
@@ -28934,6 +28930,10 @@ namespace ts {
2893428930
}
2893528931
else {
2893628932
markExportAsReferenced(node);
28933+
const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
28934+
if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) {
28935+
checkExpressionCached(node.propertyName || node.name);
28936+
}
2893728937
}
2893828938
}
2893928939
}
@@ -28960,7 +28960,17 @@ namespace ts {
2896028960
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
2896128961
}
2896228962
if (node.expression.kind === SyntaxKind.Identifier) {
28963-
markExportAsReferenced(node);
28963+
const id = node.expression as Identifier;
28964+
const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node);
28965+
if (sym) {
28966+
markAliasReferenced(sym, id);
28967+
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
28968+
const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym;
28969+
if (target === unknownSymbol || target.flags & SymbolFlags.Value) {
28970+
// However if it is a value, we need to check it's being used correctly
28971+
checkExpressionCached(node.expression);
28972+
}
28973+
}
2896428974

2896528975
if (getEmitDeclarations(compilerOptions)) {
2896628976
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)