Skip to content

Commit a625dec

Browse files
authored
Elide import namespace from which only const enums are used (microsoft#20320)
1 parent 78250ec commit a625dec

5 files changed

Lines changed: 144 additions & 0 deletions

File tree

src/compiler/checker.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15581,6 +15581,8 @@ namespace ts {
1558115581

1558215582
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
1558315583
let propType: Type;
15584+
let leftSymbol = getNodeLinks(left) && getNodeLinks(left).resolvedSymbol;
15585+
const leftWasReferenced = leftSymbol && getSymbolLinks(leftSymbol).referenced;
1558415586
const leftType = checkNonNullExpression(left);
1558515587
const apparentType = getApparentType(getWidenedType(leftType));
1558615588
if (isTypeAny(apparentType) || apparentType === silentNeverType) {
@@ -15604,6 +15606,13 @@ namespace ts {
1560415606
else {
1560515607
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
1560615608
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
15609+
// Reset the referenced-ness of the LHS expression if this access refers to a const enum or const enum only module
15610+
leftSymbol = getNodeLinks(left) && getNodeLinks(left).resolvedSymbol;
15611+
if (leftSymbol && !leftWasReferenced && getSymbolLinks(leftSymbol).referenced &&
15612+
!(isNonLocalAlias(leftSymbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(prop))
15613+
) {
15614+
getSymbolLinks(leftSymbol).referenced = undefined;
15615+
}
1560715616
getNodeLinks(node).resolvedSymbol = prop;
1560815617
checkPropertyAccessibility(node, left, apparentType, prop);
1560915618
if (assignmentKind) {
@@ -24691,6 +24700,11 @@ namespace ts {
2469124700
if (symbol && getSymbolLinks(symbol).referenced) {
2469224701
return true;
2469324702
}
24703+
const target = getSymbolLinks(symbol).target;
24704+
if (target && getModifierFlags(node) & ModifierFlags.Export && target.flags & SymbolFlags.Value) {
24705+
// An `export import ... =` of a value symbol is always considered referenced
24706+
return true;
24707+
}
2469424708
}
2469524709

2469624710
if (checkChildren) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [tests/cases/compiler/constEnumNamespaceReferenceCausesNoImport.ts] ////
2+
3+
//// [foo.ts]
4+
export const enum ConstFooEnum {
5+
Some,
6+
Values,
7+
Here
8+
};
9+
export function fooFunc(): void { /* removed */ }
10+
//// [index.ts]
11+
import * as Foo from "./foo";
12+
13+
function check(x: Foo.ConstFooEnum): void {
14+
switch (x) {
15+
case Foo.ConstFooEnum.Some:
16+
break;
17+
}
18+
}
19+
20+
//// [foo.js]
21+
"use strict";
22+
exports.__esModule = true;
23+
;
24+
function fooFunc() { }
25+
exports.fooFunc = fooFunc;
26+
//// [index.js]
27+
"use strict";
28+
exports.__esModule = true;
29+
function check(x) {
30+
switch (x) {
31+
case 0 /* Some */:
32+
break;
33+
}
34+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/compiler/foo.ts ===
2+
export const enum ConstFooEnum {
3+
>ConstFooEnum : Symbol(ConstFooEnum, Decl(foo.ts, 0, 0))
4+
5+
Some,
6+
>Some : Symbol(ConstFooEnum.Some, Decl(foo.ts, 0, 32))
7+
8+
Values,
9+
>Values : Symbol(ConstFooEnum.Values, Decl(foo.ts, 1, 9))
10+
11+
Here
12+
>Here : Symbol(ConstFooEnum.Here, Decl(foo.ts, 2, 11))
13+
14+
};
15+
export function fooFunc(): void { /* removed */ }
16+
>fooFunc : Symbol(fooFunc, Decl(foo.ts, 4, 2))
17+
18+
=== tests/cases/compiler/index.ts ===
19+
import * as Foo from "./foo";
20+
>Foo : Symbol(Foo, Decl(index.ts, 0, 6))
21+
22+
function check(x: Foo.ConstFooEnum): void {
23+
>check : Symbol(check, Decl(index.ts, 0, 29))
24+
>x : Symbol(x, Decl(index.ts, 2, 15))
25+
>Foo : Symbol(Foo, Decl(index.ts, 0, 6))
26+
>ConstFooEnum : Symbol(Foo.ConstFooEnum, Decl(foo.ts, 0, 0))
27+
28+
switch (x) {
29+
>x : Symbol(x, Decl(index.ts, 2, 15))
30+
31+
case Foo.ConstFooEnum.Some:
32+
>Foo.ConstFooEnum.Some : Symbol(Foo.ConstFooEnum.Some, Decl(foo.ts, 0, 32))
33+
>Foo.ConstFooEnum : Symbol(Foo.ConstFooEnum, Decl(foo.ts, 0, 0))
34+
>Foo : Symbol(Foo, Decl(index.ts, 0, 6))
35+
>ConstFooEnum : Symbol(Foo.ConstFooEnum, Decl(foo.ts, 0, 0))
36+
>Some : Symbol(Foo.ConstFooEnum.Some, Decl(foo.ts, 0, 32))
37+
38+
break;
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/compiler/foo.ts ===
2+
export const enum ConstFooEnum {
3+
>ConstFooEnum : ConstFooEnum
4+
5+
Some,
6+
>Some : ConstFooEnum.Some
7+
8+
Values,
9+
>Values : ConstFooEnum.Values
10+
11+
Here
12+
>Here : ConstFooEnum.Here
13+
14+
};
15+
export function fooFunc(): void { /* removed */ }
16+
>fooFunc : () => void
17+
18+
=== tests/cases/compiler/index.ts ===
19+
import * as Foo from "./foo";
20+
>Foo : typeof Foo
21+
22+
function check(x: Foo.ConstFooEnum): void {
23+
>check : (x: Foo.ConstFooEnum) => void
24+
>x : Foo.ConstFooEnum
25+
>Foo : any
26+
>ConstFooEnum : Foo.ConstFooEnum
27+
28+
switch (x) {
29+
>x : Foo.ConstFooEnum
30+
31+
case Foo.ConstFooEnum.Some:
32+
>Foo.ConstFooEnum.Some : Foo.ConstFooEnum.Some
33+
>Foo.ConstFooEnum : typeof Foo.ConstFooEnum
34+
>Foo : typeof Foo
35+
>ConstFooEnum : typeof Foo.ConstFooEnum
36+
>Some : Foo.ConstFooEnum.Some
37+
38+
break;
39+
}
40+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @filename: foo.ts
2+
export const enum ConstFooEnum {
3+
Some,
4+
Values,
5+
Here
6+
};
7+
export function fooFunc(): void { /* removed */ }
8+
// @filename: index.ts
9+
import * as Foo from "./foo";
10+
11+
function check(x: Foo.ConstFooEnum): void {
12+
switch (x) {
13+
case Foo.ConstFooEnum.Some:
14+
break;
15+
}
16+
}

0 commit comments

Comments
 (0)