Skip to content

Commit 3b54ffc

Browse files
authored
Preserve const enums should keep import refs (microsoft#28498)
* 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 * Only mark const enums as references with preserveConstEnums on in export assignments * Limit change to declarations and preserveConstEnums mode
1 parent 4df2fc6 commit 3b54ffc

20 files changed

+615
-20
lines changed

src/compiler/checker.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,7 +2380,7 @@ namespace ts {
23802380
return links.target;
23812381
}
23822382

2383-
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
2383+
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) {
23842384
const symbol = getSymbolOfNode(node);
23852385
const target = resolveAlias(symbol);
23862386
if (target) {
@@ -2402,15 +2402,10 @@ namespace ts {
24022402
links.referenced = true;
24032403
const node = getDeclarationOfAliasSymbol(symbol);
24042404
if (!node) return Debug.fail();
2405-
if (node.kind === SyntaxKind.ExportAssignment) {
2406-
// export default <symbol>
2407-
checkExpressionCached((<ExportAssignment>node).expression);
2408-
}
2409-
else if (node.kind === SyntaxKind.ExportSpecifier) {
2410-
// export { <symbol> } or export { <symbol> as foo }
2411-
checkExpressionCached((<ExportSpecifier>node).propertyName || (<ExportSpecifier>node).name);
2412-
}
2413-
else if (isInternalModuleImportEqualsDeclaration(node)) {
2405+
// We defer checking of the reference of an `import =` until the import itself is referenced,
2406+
// This way a chain of imports can be elided if ultimately the final input is only used in a type
2407+
// position.
2408+
if (isInternalModuleImportEqualsDeclaration(node)) {
24142409
// import foo = <symbol>
24152410
checkExpressionCached(<Expression>node.moduleReference);
24162411
}
@@ -17833,8 +17828,12 @@ namespace ts {
1783317828
return type;
1783417829
}
1783517830

17831+
function isExportOrExportExpression(location: Node) {
17832+
return !!findAncestor(location, e => e.parent && isExportAssignment(e.parent) && e.parent.expression === e && isEntityNameExpression(e));
17833+
}
17834+
1783617835
function markAliasReferenced(symbol: Symbol, location: Node) {
17837-
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
17836+
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) {
1783817837
markAliasSymbolAsReferenced(symbol);
1783917838
}
1784017839
}
@@ -20345,8 +20344,8 @@ namespace ts {
2034520344
// if jsx emit was not react as there wont be error being emitted
2034620345
reactSym.isReferenced = SymbolFlags.All;
2034720346

20348-
// If react symbol is alias, mark it as referenced
20349-
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
20347+
// If react symbol is alias, mark it as refereced
20348+
if (reactSym.flags & SymbolFlags.Alias) {
2035020349
markAliasSymbolAsReferenced(reactSym);
2035120350
}
2035220351
}
@@ -24897,7 +24896,7 @@ namespace ts {
2489724896
return result;
2489824897
}
2489924898

24900-
function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type {
24899+
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
2490124900
const links = getNodeLinks(node);
2490224901
if (!links.resolvedType) {
2490324902
if (checkMode && checkMode !== CheckMode.Normal) {
@@ -25225,7 +25224,8 @@ namespace ts {
2522525224
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
2522625225
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
2522725226
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node) ||
25228-
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node));
25227+
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node)) ||
25228+
(node.parent.kind === SyntaxKind.ExportSpecifier && (compilerOptions.preserveConstEnums || node.flags & NodeFlags.Ambient)); // We allow reexporting const enums
2522925229

2523025230
if (!ok) {
2523125231
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);
@@ -30153,6 +30153,10 @@ namespace ts {
3015330153
}
3015430154
else {
3015530155
markExportAsReferenced(node);
30156+
const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
30157+
if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) {
30158+
checkExpressionCached(node.propertyName || node.name);
30159+
}
3015630160
}
3015730161
}
3015830162
}
@@ -30179,7 +30183,17 @@ namespace ts {
3017930183
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
3018030184
}
3018130185
if (node.expression.kind === SyntaxKind.Identifier) {
30182-
markExportAsReferenced(node);
30186+
const id = node.expression as Identifier;
30187+
const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node);
30188+
if (sym) {
30189+
markAliasReferenced(sym, id);
30190+
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
30191+
const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym;
30192+
if (target === unknownSymbol || target.flags & SymbolFlags.Value) {
30193+
// However if it is a value, we need to check it's being used correctly
30194+
checkExpressionCached(node.expression);
30195+
}
30196+
}
3018330197

3018430198
if (getEmitDeclarations(compilerOptions)) {
3018530199
collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/amdModuleConstEnumUsage.ts] ////
2+
3+
//// [cc.ts]
4+
export const enum CharCode {
5+
A,
6+
B
7+
}
8+
//// [file.ts]
9+
import { CharCode } from 'defs/cc';
10+
export class User {
11+
method(input: number) {
12+
if (CharCode.A === input) {}
13+
}
14+
}
15+
16+
17+
//// [cc.js]
18+
define(["require", "exports"], function (require, exports) {
19+
"use strict";
20+
exports.__esModule = true;
21+
var CharCode;
22+
(function (CharCode) {
23+
CharCode[CharCode["A"] = 0] = "A";
24+
CharCode[CharCode["B"] = 1] = "B";
25+
})(CharCode = exports.CharCode || (exports.CharCode = {}));
26+
});
27+
//// [file.js]
28+
define(["require", "exports"], function (require, exports) {
29+
"use strict";
30+
exports.__esModule = true;
31+
var User = /** @class */ (function () {
32+
function User() {
33+
}
34+
User.prototype.method = function (input) {
35+
if (0 /* A */ === input) { }
36+
};
37+
return User;
38+
}());
39+
exports.User = User;
40+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== /proj/defs/cc.ts ===
2+
export const enum CharCode {
3+
>CharCode : Symbol(CharCode, Decl(cc.ts, 0, 0))
4+
5+
A,
6+
>A : Symbol(CharCode.A, Decl(cc.ts, 0, 28))
7+
8+
B
9+
>B : Symbol(CharCode.B, Decl(cc.ts, 1, 6))
10+
}
11+
=== /proj/component/file.ts ===
12+
import { CharCode } from 'defs/cc';
13+
>CharCode : Symbol(CharCode, Decl(file.ts, 0, 8))
14+
15+
export class User {
16+
>User : Symbol(User, Decl(file.ts, 0, 35))
17+
18+
method(input: number) {
19+
>method : Symbol(User.method, Decl(file.ts, 1, 19))
20+
>input : Symbol(input, Decl(file.ts, 2, 11))
21+
22+
if (CharCode.A === input) {}
23+
>CharCode.A : Symbol(CharCode.A, Decl(cc.ts, 0, 28))
24+
>CharCode : Symbol(CharCode, Decl(file.ts, 0, 8))
25+
>A : Symbol(CharCode.A, Decl(cc.ts, 0, 28))
26+
>input : Symbol(input, Decl(file.ts, 2, 11))
27+
}
28+
}
29+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== /proj/defs/cc.ts ===
2+
export const enum CharCode {
3+
>CharCode : CharCode
4+
5+
A,
6+
>A : CharCode.A
7+
8+
B
9+
>B : CharCode.B
10+
}
11+
=== /proj/component/file.ts ===
12+
import { CharCode } from 'defs/cc';
13+
>CharCode : typeof CharCode
14+
15+
export class User {
16+
>User : User
17+
18+
method(input: number) {
19+
>method : (input: number) => void
20+
>input : number
21+
22+
if (CharCode.A === input) {}
23+
>CharCode.A === input : boolean
24+
>CharCode.A : CharCode.A
25+
>CharCode : typeof CharCode
26+
>A : CharCode.A
27+
>input : number
28+
}
29+
}
30+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [tests/cases/compiler/constEnumNoPreserveDeclarationReexport.ts] ////
2+
3+
//// [ConstEnum.d.ts]
4+
export const enum MyConstEnum {
5+
Foo,
6+
Bar
7+
}
8+
//// [ImportExport.d.ts]
9+
import { MyConstEnum } from './ConstEnum';
10+
export default MyConstEnum;
11+
//// [ReExport.d.ts]
12+
export { MyConstEnum as default } from './ConstEnum';
13+
//// [usages.ts]
14+
import {MyConstEnum} from "./ConstEnum";
15+
import AlsoEnum from "./ImportExport";
16+
import StillEnum from "./ReExport";
17+
18+
MyConstEnum.Foo;
19+
AlsoEnum.Foo;
20+
StillEnum.Foo;
21+
22+
23+
//// [usages.js]
24+
"use strict";
25+
exports.__esModule = true;
26+
0 /* Foo */;
27+
0 /* Foo */;
28+
0 /* Foo */;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/ConstEnum.d.ts ===
2+
export const enum MyConstEnum {
3+
>MyConstEnum : Symbol(MyConstEnum, Decl(ConstEnum.d.ts, 0, 0))
4+
5+
Foo,
6+
>Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
7+
8+
Bar
9+
>Bar : Symbol(MyConstEnum.Bar, Decl(ConstEnum.d.ts, 1, 8))
10+
}
11+
=== tests/cases/compiler/ImportExport.d.ts ===
12+
import { MyConstEnum } from './ConstEnum';
13+
>MyConstEnum : Symbol(MyConstEnum, Decl(ImportExport.d.ts, 0, 8))
14+
15+
export default MyConstEnum;
16+
>MyConstEnum : Symbol(MyConstEnum, Decl(ImportExport.d.ts, 0, 8))
17+
18+
=== tests/cases/compiler/ReExport.d.ts ===
19+
export { MyConstEnum as default } from './ConstEnum';
20+
>MyConstEnum : Symbol(MyConstEnum, Decl(ConstEnum.d.ts, 0, 0))
21+
>default : Symbol(default, Decl(ReExport.d.ts, 0, 8))
22+
23+
=== tests/cases/compiler/usages.ts ===
24+
import {MyConstEnum} from "./ConstEnum";
25+
>MyConstEnum : Symbol(MyConstEnum, Decl(usages.ts, 0, 8))
26+
27+
import AlsoEnum from "./ImportExport";
28+
>AlsoEnum : Symbol(AlsoEnum, Decl(usages.ts, 1, 6))
29+
30+
import StillEnum from "./ReExport";
31+
>StillEnum : Symbol(StillEnum, Decl(usages.ts, 2, 6))
32+
33+
MyConstEnum.Foo;
34+
>MyConstEnum.Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
35+
>MyConstEnum : Symbol(MyConstEnum, Decl(usages.ts, 0, 8))
36+
>Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
37+
38+
AlsoEnum.Foo;
39+
>AlsoEnum.Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
40+
>AlsoEnum : Symbol(AlsoEnum, Decl(usages.ts, 1, 6))
41+
>Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
42+
43+
StillEnum.Foo;
44+
>StillEnum.Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
45+
>StillEnum : Symbol(StillEnum, Decl(usages.ts, 2, 6))
46+
>Foo : Symbol(MyConstEnum.Foo, Decl(ConstEnum.d.ts, 0, 31))
47+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/ConstEnum.d.ts ===
2+
export const enum MyConstEnum {
3+
>MyConstEnum : MyConstEnum
4+
5+
Foo,
6+
>Foo : MyConstEnum
7+
8+
Bar
9+
>Bar : MyConstEnum
10+
}
11+
=== tests/cases/compiler/ImportExport.d.ts ===
12+
import { MyConstEnum } from './ConstEnum';
13+
>MyConstEnum : typeof MyConstEnum
14+
15+
export default MyConstEnum;
16+
>MyConstEnum : MyConstEnum
17+
18+
=== tests/cases/compiler/ReExport.d.ts ===
19+
export { MyConstEnum as default } from './ConstEnum';
20+
>MyConstEnum : typeof import("tests/cases/compiler/ConstEnum").MyConstEnum
21+
>default : typeof import("tests/cases/compiler/ConstEnum").MyConstEnum
22+
23+
=== tests/cases/compiler/usages.ts ===
24+
import {MyConstEnum} from "./ConstEnum";
25+
>MyConstEnum : typeof MyConstEnum
26+
27+
import AlsoEnum from "./ImportExport";
28+
>AlsoEnum : typeof MyConstEnum
29+
30+
import StillEnum from "./ReExport";
31+
>StillEnum : typeof MyConstEnum
32+
33+
MyConstEnum.Foo;
34+
>MyConstEnum.Foo : MyConstEnum
35+
>MyConstEnum : typeof MyConstEnum
36+
>Foo : MyConstEnum
37+
38+
AlsoEnum.Foo;
39+
>AlsoEnum.Foo : MyConstEnum
40+
>AlsoEnum : typeof MyConstEnum
41+
>Foo : MyConstEnum
42+
43+
StillEnum.Foo;
44+
>StillEnum.Foo : MyConstEnum
45+
>StillEnum : typeof MyConstEnum
46+
>Foo : MyConstEnum
47+
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+

0 commit comments

Comments
 (0)