Skip to content

Commit 0f3809e

Browse files
authored
fix: enum self reference (microsoft#47287)
* fix: enum self reference * fix: add test case * chore: clean code * fix: check `errorType` directly * chore: revert refactor of `isConstantMemberAccess`
1 parent 53b96d9 commit 0f3809e

File tree

12 files changed

+416
-8
lines changed

12 files changed

+416
-8
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39476,16 +39476,15 @@ namespace ts {
3947639476
return nodeIsMissing(expr) ? 0 : evaluateEnumMember(expr, getSymbolOfNode(member.parent), identifier.escapedText);
3947739477
case SyntaxKind.ElementAccessExpression:
3947839478
case SyntaxKind.PropertyAccessExpression:
39479-
const ex = expr as AccessExpression;
39480-
if (isConstantMemberAccess(ex)) {
39481-
const type = getTypeOfExpression(ex.expression);
39479+
if (isConstantMemberAccess(expr)) {
39480+
const type = getTypeOfExpression(expr.expression);
3948239481
if (type.symbol && type.symbol.flags & SymbolFlags.Enum) {
3948339482
let name: __String;
39484-
if (ex.kind === SyntaxKind.PropertyAccessExpression) {
39485-
name = ex.name.escapedText;
39483+
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
39484+
name = expr.name.escapedText;
3948639485
}
3948739486
else {
39488-
name = escapeLeadingUnderscores(cast(ex.argumentExpression, isLiteralExpression).text);
39487+
name = escapeLeadingUnderscores(cast(expr.argumentExpression, isLiteralExpression).text);
3948939488
}
3949039489
return evaluateEnumMember(expr, type.symbol, name);
3949139490
}
@@ -39514,7 +39513,12 @@ namespace ts {
3951439513
}
3951539514
}
3951639515

39517-
function isConstantMemberAccess(node: Expression): boolean {
39516+
function isConstantMemberAccess(node: Expression): node is AccessExpression {
39517+
const type = getTypeOfExpression(node);
39518+
if(type === errorType) {
39519+
return false;
39520+
}
39521+
3951839522
return node.kind === SyntaxKind.Identifier ||
3951939523
node.kind === SyntaxKind.PropertyAccessExpression && isConstantMemberAccess((node as PropertyAccessExpression).expression) ||
3952039524
node.kind === SyntaxKind.ElementAccessExpression && isConstantMemberAccess((node as ElementAccessExpression).expression) &&

tests/baselines/reference/constEnumErrors.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ tests/cases/compiler/constEnumErrors.ts(1,12): error TS2567: Enum declarations c
22
tests/cases/compiler/constEnumErrors.ts(5,8): error TS2567: Enum declarations can only merge with namespace or other enum declarations.
33
tests/cases/compiler/constEnumErrors.ts(12,9): error TS2651: A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.
44
tests/cases/compiler/constEnumErrors.ts(14,9): error TS2474: const enum member initializers can only contain literal values and other computed enum values.
5+
tests/cases/compiler/constEnumErrors.ts(14,12): error TS2339: Property 'Z' does not exist on type 'typeof E1'.
56
tests/cases/compiler/constEnumErrors.ts(15,10): error TS2474: const enum member initializers can only contain literal values and other computed enum values.
7+
tests/cases/compiler/constEnumErrors.ts(15,13): error TS2339: Property 'Z' does not exist on type 'typeof E1'.
68
tests/cases/compiler/constEnumErrors.ts(22,13): error TS2476: A const enum member can only be accessed using a string literal.
79
tests/cases/compiler/constEnumErrors.ts(24,13): error TS2476: A const enum member can only be accessed using a string literal.
810
tests/cases/compiler/constEnumErrors.ts(25,13): error TS2476: A const enum member can only be accessed using a string literal.
@@ -14,7 +16,7 @@ tests/cases/compiler/constEnumErrors.ts(42,9): error TS2477: 'const' enum member
1416
tests/cases/compiler/constEnumErrors.ts(43,9): error TS2478: 'const' enum member initializer was evaluated to disallowed value 'NaN'.
1517

1618

17-
==== tests/cases/compiler/constEnumErrors.ts (14 errors) ====
19+
==== tests/cases/compiler/constEnumErrors.ts (16 errors) ====
1820
const enum E {
1921
~
2022
!!! error TS2567: Enum declarations can only merge with namespace or other enum declarations.
@@ -37,9 +39,13 @@ tests/cases/compiler/constEnumErrors.ts(43,9): error TS2478: 'const' enum member
3739
Y = E1.Z,
3840
~~~~
3941
!!! error TS2474: const enum member initializers can only contain literal values and other computed enum values.
42+
~
43+
!!! error TS2339: Property 'Z' does not exist on type 'typeof E1'.
4044
Y1 = E1["Z"]
4145
~~~~~~~
4246
!!! error TS2474: const enum member initializers can only contain literal values and other computed enum values.
47+
~~~
48+
!!! error TS2339: Property 'Z' does not exist on type 'typeof E1'.
4349
}
4450

4551
const enum E2 {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
tests/cases/compiler/enumBasics2.ts(4,9): error TS2339: Property 'b' does not exist on type 'Foo'.
2+
tests/cases/compiler/enumBasics2.ts(5,9): error TS2339: Property 'a' does not exist on type 'Foo'.
3+
tests/cases/compiler/enumBasics2.ts(6,9): error TS2339: Property 'x' does not exist on type 'Foo'.
4+
tests/cases/compiler/enumBasics2.ts(6,15): error TS2339: Property 'x' does not exist on type 'Foo'.
5+
tests/cases/compiler/enumBasics2.ts(13,13): error TS2339: Property 'a' does not exist on type 'Foo'.
6+
7+
8+
==== tests/cases/compiler/enumBasics2.ts (5 errors) ====
9+
enum Foo {
10+
a = 2,
11+
b = 3,
12+
x = a.b, // should error
13+
~
14+
!!! error TS2339: Property 'b' does not exist on type 'Foo'.
15+
y = b.a, // should error
16+
~
17+
!!! error TS2339: Property 'a' does not exist on type 'Foo'.
18+
z = y.x * a.x, // should error
19+
~
20+
!!! error TS2339: Property 'x' does not exist on type 'Foo'.
21+
~
22+
!!! error TS2339: Property 'x' does not exist on type 'Foo'.
23+
}
24+
25+
enum Bar {
26+
a = (1).valueOf(), // ok
27+
b = Foo.a, // ok
28+
c = Foo.a.valueOf(), // ok
29+
d = Foo.a.a, // should error
30+
~
31+
!!! error TS2339: Property 'a' does not exist on type 'Foo'.
32+
}
33+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [enumBasics2.ts]
2+
enum Foo {
3+
a = 2,
4+
b = 3,
5+
x = a.b, // should error
6+
y = b.a, // should error
7+
z = y.x * a.x, // should error
8+
}
9+
10+
enum Bar {
11+
a = (1).valueOf(), // ok
12+
b = Foo.a, // ok
13+
c = Foo.a.valueOf(), // ok
14+
d = Foo.a.a, // should error
15+
}
16+
17+
18+
//// [enumBasics2.js]
19+
var Foo;
20+
(function (Foo) {
21+
Foo[Foo["a"] = 2] = "a";
22+
Foo[Foo["b"] = 3] = "b";
23+
Foo[Foo["x"] = Foo.a.b] = "x";
24+
Foo[Foo["y"] = Foo.b.a] = "y";
25+
Foo[Foo["z"] = Foo.y.x * Foo.a.x] = "z";
26+
})(Foo || (Foo = {}));
27+
var Bar;
28+
(function (Bar) {
29+
Bar[Bar["a"] = (1).valueOf()] = "a";
30+
Bar[Bar["b"] = 2] = "b";
31+
Bar[Bar["c"] = Foo.a.valueOf()] = "c";
32+
Bar[Bar["d"] = Foo.a.a] = "d";
33+
})(Bar || (Bar = {}));
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
=== tests/cases/compiler/enumBasics2.ts ===
2+
enum Foo {
3+
>Foo : Symbol(Foo, Decl(enumBasics2.ts, 0, 0))
4+
5+
a = 2,
6+
>a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
7+
8+
b = 3,
9+
>b : Symbol(Foo.b, Decl(enumBasics2.ts, 1, 8))
10+
11+
x = a.b, // should error
12+
>x : Symbol(Foo.x, Decl(enumBasics2.ts, 2, 8))
13+
>a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
14+
15+
y = b.a, // should error
16+
>y : Symbol(Foo.y, Decl(enumBasics2.ts, 3, 10))
17+
>b : Symbol(Foo.b, Decl(enumBasics2.ts, 1, 8))
18+
19+
z = y.x * a.x, // should error
20+
>z : Symbol(Foo.z, Decl(enumBasics2.ts, 4, 10))
21+
>y : Symbol(Foo.y, Decl(enumBasics2.ts, 3, 10))
22+
>a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
23+
}
24+
25+
enum Bar {
26+
>Bar : Symbol(Bar, Decl(enumBasics2.ts, 6, 1))
27+
28+
a = (1).valueOf(), // ok
29+
>a : Symbol(Bar.a, Decl(enumBasics2.ts, 8, 10))
30+
>(1).valueOf : Symbol(Number.valueOf, Decl(lib.es5.d.ts, --, --))
31+
>valueOf : Symbol(Number.valueOf, Decl(lib.es5.d.ts, --, --))
32+
33+
b = Foo.a, // ok
34+
>b : Symbol(Bar.b, Decl(enumBasics2.ts, 9, 20))
35+
>Foo.a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
36+
>Foo : Symbol(Foo, Decl(enumBasics2.ts, 0, 0))
37+
>a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
38+
39+
c = Foo.a.valueOf(), // ok
40+
>c : Symbol(Bar.c, Decl(enumBasics2.ts, 10, 12))
41+
>Foo.a.valueOf : Symbol(Number.valueOf, Decl(lib.es5.d.ts, --, --))
42+
>Foo.a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
43+
>Foo : Symbol(Foo, Decl(enumBasics2.ts, 0, 0))
44+
>a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
45+
>valueOf : Symbol(Number.valueOf, Decl(lib.es5.d.ts, --, --))
46+
47+
d = Foo.a.a, // should error
48+
>d : Symbol(Bar.d, Decl(enumBasics2.ts, 11, 22))
49+
>Foo.a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
50+
>Foo : Symbol(Foo, Decl(enumBasics2.ts, 0, 0))
51+
>a : Symbol(Foo.a, Decl(enumBasics2.ts, 0, 10))
52+
}
53+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
=== tests/cases/compiler/enumBasics2.ts ===
2+
enum Foo {
3+
>Foo : Foo
4+
5+
a = 2,
6+
>a : Foo
7+
>2 : 2
8+
9+
b = 3,
10+
>b : Foo
11+
>3 : 3
12+
13+
x = a.b, // should error
14+
>x : Foo
15+
>a.b : any
16+
>a : Foo
17+
>b : any
18+
19+
y = b.a, // should error
20+
>y : Foo
21+
>b.a : any
22+
>b : Foo
23+
>a : any
24+
25+
z = y.x * a.x, // should error
26+
>z : Foo
27+
>y.x * a.x : number
28+
>y.x : any
29+
>y : Foo
30+
>x : any
31+
>a.x : any
32+
>a : Foo
33+
>x : any
34+
}
35+
36+
enum Bar {
37+
>Bar : Bar
38+
39+
a = (1).valueOf(), // ok
40+
>a : Bar
41+
>(1).valueOf() : number
42+
>(1).valueOf : () => number
43+
>(1) : 1
44+
>1 : 1
45+
>valueOf : () => number
46+
47+
b = Foo.a, // ok
48+
>b : Bar
49+
>Foo.a : Foo
50+
>Foo : typeof Foo
51+
>a : Foo
52+
53+
c = Foo.a.valueOf(), // ok
54+
>c : Bar
55+
>Foo.a.valueOf() : number
56+
>Foo.a.valueOf : () => number
57+
>Foo.a : Foo
58+
>Foo : typeof Foo
59+
>a : Foo
60+
>valueOf : () => number
61+
62+
d = Foo.a.a, // should error
63+
>d : Bar
64+
>Foo.a.a : any
65+
>Foo.a : Foo
66+
>Foo : typeof Foo
67+
>a : Foo
68+
>a : any
69+
}
70+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
tests/cases/compiler/enumBasics3.ts(5,13): error TS2339: Property 'a' does not exist on type 'E1'.
2+
tests/cases/compiler/enumBasics3.ts(14,20): error TS2339: Property 'a' does not exist on type 'E1'.
3+
4+
5+
==== tests/cases/compiler/enumBasics3.ts (2 errors) ====
6+
module M {
7+
export namespace N {
8+
export enum E1 {
9+
a = 1,
10+
b = a.a, // should error
11+
~
12+
!!! error TS2339: Property 'a' does not exist on type 'E1'.
13+
}
14+
}
15+
}
16+
17+
module M {
18+
export namespace N {
19+
export enum E2 {
20+
b = M.N.E1.a,
21+
c = M.N.E1.a.a, // should error
22+
~
23+
!!! error TS2339: Property 'a' does not exist on type 'E1'.
24+
}
25+
}
26+
}
27+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [enumBasics3.ts]
2+
module M {
3+
export namespace N {
4+
export enum E1 {
5+
a = 1,
6+
b = a.a, // should error
7+
}
8+
}
9+
}
10+
11+
module M {
12+
export namespace N {
13+
export enum E2 {
14+
b = M.N.E1.a,
15+
c = M.N.E1.a.a, // should error
16+
}
17+
}
18+
}
19+
20+
21+
//// [enumBasics3.js]
22+
var M;
23+
(function (M) {
24+
var N;
25+
(function (N) {
26+
var E1;
27+
(function (E1) {
28+
E1[E1["a"] = 1] = "a";
29+
E1[E1["b"] = E1.a.a] = "b";
30+
})(E1 = N.E1 || (N.E1 = {}));
31+
})(N = M.N || (M.N = {}));
32+
})(M || (M = {}));
33+
(function (M) {
34+
var N;
35+
(function (N) {
36+
var E2;
37+
(function (E2) {
38+
E2[E2["b"] = 1] = "b";
39+
E2[E2["c"] = M.N.E1.a.a] = "c";
40+
})(E2 = N.E2 || (N.E2 = {}));
41+
})(N = M.N || (M.N = {}));
42+
})(M || (M = {}));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/enumBasics3.ts ===
2+
module M {
3+
>M : Symbol(M, Decl(enumBasics3.ts, 0, 0), Decl(enumBasics3.ts, 7, 1))
4+
5+
export namespace N {
6+
>N : Symbol(N, Decl(enumBasics3.ts, 0, 10), Decl(enumBasics3.ts, 9, 10))
7+
8+
export enum E1 {
9+
>E1 : Symbol(E1, Decl(enumBasics3.ts, 1, 22))
10+
11+
a = 1,
12+
>a : Symbol(E1.a, Decl(enumBasics3.ts, 2, 20))
13+
14+
b = a.a, // should error
15+
>b : Symbol(E1.b, Decl(enumBasics3.ts, 3, 12))
16+
>a : Symbol(E1.a, Decl(enumBasics3.ts, 2, 20))
17+
}
18+
}
19+
}
20+
21+
module M {
22+
>M : Symbol(M, Decl(enumBasics3.ts, 0, 0), Decl(enumBasics3.ts, 7, 1))
23+
24+
export namespace N {
25+
>N : Symbol(N, Decl(enumBasics3.ts, 0, 10), Decl(enumBasics3.ts, 9, 10))
26+
27+
export enum E2 {
28+
>E2 : Symbol(E2, Decl(enumBasics3.ts, 10, 22))
29+
30+
b = M.N.E1.a,
31+
>b : Symbol(E2.b, Decl(enumBasics3.ts, 11, 20))
32+
>M.N.E1.a : Symbol(E1.a, Decl(enumBasics3.ts, 2, 20))
33+
>M.N.E1 : Symbol(E1, Decl(enumBasics3.ts, 1, 22))
34+
>M.N : Symbol(N, Decl(enumBasics3.ts, 0, 10), Decl(enumBasics3.ts, 9, 10))
35+
>M : Symbol(M, Decl(enumBasics3.ts, 0, 0), Decl(enumBasics3.ts, 7, 1))
36+
>N : Symbol(N, Decl(enumBasics3.ts, 0, 10), Decl(enumBasics3.ts, 9, 10))
37+
>E1 : Symbol(E1, Decl(enumBasics3.ts, 1, 22))
38+
>a : Symbol(E1.a, Decl(enumBasics3.ts, 2, 20))
39+
40+
c = M.N.E1.a.a, // should error
41+
>c : Symbol(E2.c, Decl(enumBasics3.ts, 12, 19))
42+
>M.N.E1.a : Symbol(E1.a, Decl(enumBasics3.ts, 2, 20))
43+
>M.N.E1 : Symbol(E1, Decl(enumBasics3.ts, 1, 22))
44+
>M.N : Symbol(N, Decl(enumBasics3.ts, 0, 10), Decl(enumBasics3.ts, 9, 10))
45+
>M : Symbol(M, Decl(enumBasics3.ts, 0, 0), Decl(enumBasics3.ts, 7, 1))
46+
>N : Symbol(N, Decl(enumBasics3.ts, 0, 10), Decl(enumBasics3.ts, 9, 10))
47+
>E1 : Symbol(E1, Decl(enumBasics3.ts, 1, 22))
48+
>a : Symbol(E1.a, Decl(enumBasics3.ts, 2, 20))
49+
}
50+
}
51+
}
52+

0 commit comments

Comments
 (0)