Skip to content

Commit 06e05f2

Browse files
authored
Improve error message for computed enums (microsoft#37790)
* Add error message for computed enums * Add test case for computed enums * Accept baselines * Fix returned value when error
1 parent 92cd3ae commit 06e05f2

File tree

8 files changed

+98
-54
lines changed

8 files changed

+98
-54
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33676,7 +33676,13 @@ namespace ts {
3367633676
}
3367733677
else {
3367833678
// Only here do we need to check that the initializer is assignable to the enum type.
33679-
checkTypeAssignableTo(checkExpression(initializer), getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined);
33679+
const source = checkExpression(initializer);
33680+
if (!isTypeAssignableToKind(source, TypeFlags.NumberLike)) {
33681+
error(initializer, Diagnostics.Only_numeric_enums_can_have_computed_members_but_this_expression_has_type_0_If_you_do_not_need_exhaustiveness_checks_consider_using_an_object_literal_instead, typeToString(source));
33682+
}
33683+
else {
33684+
checkTypeAssignableTo(source, getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined);
33685+
}
3368033686
}
3368133687
return value;
3368233688

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5737,5 +5737,9 @@
57375737
"The intersection '{0}' was reduced to 'never' because property '{1}' exists in multiple constituents and is private in some.": {
57385738
"category": "Error",
57395739
"code": 18032
5740+
},
5741+
"Only numeric enums can have computed members, but this expression has type '{0}'. If you do not need exhaustiveness checks, consider using an object literal instead.": {
5742+
"category": "Error",
5743+
"code": 18033
57405744
}
57415745
}

tests/baselines/reference/arrowFunctionContexts.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(2,1): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
2-
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(30,9): error TS2322: Type '() => number' is not assignable to type 'E'.
2+
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(30,9): error TS18033: Only numeric enums can have computed members, but this expression has type '() => number'. If you do not need exhaustiveness checks, consider using an object literal instead.
33
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(31,16): error TS2332: 'this' cannot be referenced in current location.
44
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(43,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
5-
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(71,13): error TS2322: Type '() => number' is not assignable to type 'E'.
5+
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(71,13): error TS18033: Only numeric enums can have computed members, but this expression has type '() => number'. If you do not need exhaustiveness checks, consider using an object literal instead.
66
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,20): error TS2332: 'this' cannot be referenced in current location.
77

88

@@ -40,7 +40,7 @@ tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,20): e
4040
enum E {
4141
x = () => 4, // Error expected
4242
~~~~~~~
43-
!!! error TS2322: Type '() => number' is not assignable to type 'E'.
43+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type '() => number'. If you do not need exhaustiveness checks, consider using an object literal instead.
4444
y = (() => this).length // error, can't use this in enum
4545
~~~~
4646
!!! error TS2332: 'this' cannot be referenced in current location.
@@ -87,7 +87,7 @@ tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,20): e
8787
enum E {
8888
x = () => 4, // Error expected
8989
~~~~~~~
90-
!!! error TS2322: Type '() => number' is not assignable to type 'E'.
90+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type '() => number'. If you do not need exhaustiveness checks, consider using an object literal instead.
9191
y = (() => this).length
9292
~~~~
9393
!!! error TS2332: 'this' cannot be referenced in current location.

tests/baselines/reference/enumErrors.errors.txt

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,30 @@ tests/cases/conformance/enums/enumErrors.ts(2,6): error TS2431: Enum name cannot
22
tests/cases/conformance/enums/enumErrors.ts(3,6): error TS2431: Enum name cannot be 'number'.
33
tests/cases/conformance/enums/enumErrors.ts(4,6): error TS2431: Enum name cannot be 'string'.
44
tests/cases/conformance/enums/enumErrors.ts(5,6): error TS2431: Enum name cannot be 'boolean'.
5-
tests/cases/conformance/enums/enumErrors.ts(9,9): error TS2322: Type 'Number' is not assignable to type 'E5'.
6-
tests/cases/conformance/enums/enumErrors.ts(26,9): error TS2322: Type 'true' is not assignable to type 'E11'.
7-
tests/cases/conformance/enums/enumErrors.ts(27,9): error TS2322: Type 'Date' is not assignable to type 'E11'.
8-
tests/cases/conformance/enums/enumErrors.ts(28,9): error TS2322: Type 'Window & typeof globalThis' is not assignable to type 'E11'.
9-
tests/cases/conformance/enums/enumErrors.ts(29,9): error TS2322: Type '{}' is not assignable to type 'E11'.
10-
tests/cases/conformance/enums/enumErrors.ts(35,9): error TS2553: Computed values are not permitted in an enum with string valued members.
5+
tests/cases/conformance/enums/enumErrors.ts(9,9): error TS18033: Only numeric enums can have computed members, but this expression has type 'Number'. If you do not need exhaustiveness checks, consider using an object literal instead.
6+
tests/cases/conformance/enums/enumErrors.ts(26,9): error TS18033: Only numeric enums can have computed members, but this expression has type 'true'. If you do not need exhaustiveness checks, consider using an object literal instead.
7+
tests/cases/conformance/enums/enumErrors.ts(27,9): error TS18033: Only numeric enums can have computed members, but this expression has type 'Date'. If you do not need exhaustiveness checks, consider using an object literal instead.
8+
tests/cases/conformance/enums/enumErrors.ts(28,9): error TS18033: Only numeric enums can have computed members, but this expression has type 'Window & typeof globalThis'. If you do not need exhaustiveness checks, consider using an object literal instead.
9+
tests/cases/conformance/enums/enumErrors.ts(29,9): error TS18033: Only numeric enums can have computed members, but this expression has type '{}'. If you do not need exhaustiveness checks, consider using an object literal instead.
10+
tests/cases/conformance/enums/enumErrors.ts(30,9): error TS18033: Only numeric enums can have computed members, but this expression has type 'string'. If you do not need exhaustiveness checks, consider using an object literal instead.
1111
tests/cases/conformance/enums/enumErrors.ts(36,9): error TS2553: Computed values are not permitted in an enum with string valued members.
1212
tests/cases/conformance/enums/enumErrors.ts(37,9): error TS2553: Computed values are not permitted in an enum with string valued members.
1313
tests/cases/conformance/enums/enumErrors.ts(38,9): error TS2553: Computed values are not permitted in an enum with string valued members.
14-
tests/cases/conformance/enums/enumErrors.ts(46,18): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
15-
tests/cases/conformance/enums/enumErrors.ts(47,24): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
16-
tests/cases/conformance/enums/enumErrors.ts(47,26): error TS2452: An enum member cannot have a numeric name.
17-
tests/cases/conformance/enums/enumErrors.ts(48,28): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
18-
tests/cases/conformance/enums/enumErrors.ts(48,30): error TS2452: An enum member cannot have a numeric name.
19-
tests/cases/conformance/enums/enumErrors.ts(48,31): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
20-
tests/cases/conformance/enums/enumErrors.ts(51,16): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
21-
tests/cases/conformance/enums/enumErrors.ts(51,22): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
22-
tests/cases/conformance/enums/enumErrors.ts(51,30): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
23-
tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member cannot have a numeric name.
14+
tests/cases/conformance/enums/enumErrors.ts(39,9): error TS2553: Computed values are not permitted in an enum with string valued members.
15+
tests/cases/conformance/enums/enumErrors.ts(40,9): error TS2553: Computed values are not permitted in an enum with string valued members.
16+
tests/cases/conformance/enums/enumErrors.ts(48,18): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
17+
tests/cases/conformance/enums/enumErrors.ts(49,24): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
18+
tests/cases/conformance/enums/enumErrors.ts(49,26): error TS2452: An enum member cannot have a numeric name.
19+
tests/cases/conformance/enums/enumErrors.ts(50,28): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
20+
tests/cases/conformance/enums/enumErrors.ts(50,30): error TS2452: An enum member cannot have a numeric name.
21+
tests/cases/conformance/enums/enumErrors.ts(50,31): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
22+
tests/cases/conformance/enums/enumErrors.ts(53,16): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
23+
tests/cases/conformance/enums/enumErrors.ts(53,22): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
24+
tests/cases/conformance/enums/enumErrors.ts(53,30): error TS1357: An enum member name must be followed by a ',', '=', or '}'.
25+
tests/cases/conformance/enums/enumErrors.ts(53,33): error TS2452: An enum member cannot have a numeric name.
2426

2527

26-
==== tests/cases/conformance/enums/enumErrors.ts (23 errors) ====
28+
==== tests/cases/conformance/enums/enumErrors.ts (25 errors) ====
2729
// Enum named with PredefinedTypes
2830
enum any { }
2931
~~~
@@ -42,7 +44,7 @@ tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member
4244
enum E5 {
4345
C = new Number(30)
4446
~~~~~~~~~~~~~~
45-
!!! error TS2322: Type 'Number' is not assignable to type 'E5'.
47+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type 'Number'. If you do not need exhaustiveness checks, consider using an object literal instead.
4648
}
4749

4850
enum E9 {
@@ -61,16 +63,19 @@ tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member
6163
enum E11 {
6264
A = true,
6365
~~~~
64-
!!! error TS2322: Type 'true' is not assignable to type 'E11'.
66+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type 'true'. If you do not need exhaustiveness checks, consider using an object literal instead.
6567
B = new Date(),
6668
~~~~~~~~~~
67-
!!! error TS2322: Type 'Date' is not assignable to type 'E11'.
69+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type 'Date'. If you do not need exhaustiveness checks, consider using an object literal instead.
6870
C = window,
6971
~~~~~~
70-
!!! error TS2322: Type 'Window & typeof globalThis' is not assignable to type 'E11'.
71-
D = {}
72+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type 'Window & typeof globalThis'. If you do not need exhaustiveness checks, consider using an object literal instead.
73+
D = {},
7274
~~
73-
!!! error TS2322: Type '{}' is not assignable to type 'E11'.
75+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type '{}'. If you do not need exhaustiveness checks, consider using an object literal instead.
76+
E = (() => 'foo')(),
77+
~~~~~~~~~~~~~~~
78+
!!! error TS18033: Only numeric enums can have computed members, but this expression has type 'string'. If you do not need exhaustiveness checks, consider using an object literal instead.
7479
}
7580

7681
// Enum with string valued member and computed member initializers
@@ -87,6 +92,9 @@ tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member
8792
!!! error TS2553: Computed values are not permitted in an enum with string valued members.
8893
E = 1 + 1,
8994
~~~~~
95+
!!! error TS2553: Computed values are not permitted in an enum with string valued members.
96+
F = (() => 'foo')(),
97+
~~~~~~~~~~~~~~~
9098
!!! error TS2553: Computed values are not permitted in an enum with string valued members.
9199
}
92100

tests/baselines/reference/enumErrors.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ enum E11 {
2727
A = true,
2828
B = new Date(),
2929
C = window,
30-
D = {}
30+
D = {},
31+
E = (() => 'foo')(),
3132
}
3233

3334
// Enum with string valued member and computed member initializers
@@ -37,6 +38,7 @@ enum E12 {
3738
C = window,
3839
D = {},
3940
E = 1 + 1,
41+
F = (() => 'foo')(),
4042
}
4143

4244
// Enum with incorrect syntax
@@ -90,6 +92,7 @@ var E11;
9092
E11[E11["B"] = new Date()] = "B";
9193
E11[E11["C"] = window] = "C";
9294
E11[E11["D"] = {}] = "D";
95+
E11[E11["E"] = (function () { return 'foo'; })()] = "E";
9396
})(E11 || (E11 = {}));
9497
// Enum with string valued member and computed member initializers
9598
var E12;
@@ -99,6 +102,7 @@ var E12;
99102
E12[E12["C"] = 0] = "C";
100103
E12[E12["D"] = 0] = "D";
101104
E12[E12["E"] = 0] = "E";
105+
E12[E12["F"] = 0] = "F";
102106
})(E12 || (E12 = {}));
103107
// Enum with incorrect syntax
104108
var E13;

tests/baselines/reference/enumErrors.symbols

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,62 +65,68 @@ enum E11 {
6565
>C : Symbol(E11.C, Decl(enumErrors.ts, 26, 19))
6666
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
6767

68-
D = {}
68+
D = {},
6969
>D : Symbol(E11.D, Decl(enumErrors.ts, 27, 15))
70+
71+
E = (() => 'foo')(),
72+
>E : Symbol(E11.E, Decl(enumErrors.ts, 28, 11))
7073
}
7174

7275
// Enum with string valued member and computed member initializers
7376
enum E12 {
74-
>E12 : Symbol(E12, Decl(enumErrors.ts, 29, 1))
77+
>E12 : Symbol(E12, Decl(enumErrors.ts, 30, 1))
7578

7679
A = '',
77-
>A : Symbol(E12.A, Decl(enumErrors.ts, 32, 10))
80+
>A : Symbol(E12.A, Decl(enumErrors.ts, 33, 10))
7881

7982
B = new Date(),
80-
>B : Symbol(E12.B, Decl(enumErrors.ts, 33, 11))
83+
>B : Symbol(E12.B, Decl(enumErrors.ts, 34, 11))
8184
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
8285

8386
C = window,
84-
>C : Symbol(E12.C, Decl(enumErrors.ts, 34, 19))
87+
>C : Symbol(E12.C, Decl(enumErrors.ts, 35, 19))
8588
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
8689

8790
D = {},
88-
>D : Symbol(E12.D, Decl(enumErrors.ts, 35, 15))
91+
>D : Symbol(E12.D, Decl(enumErrors.ts, 36, 15))
8992

9093
E = 1 + 1,
91-
>E : Symbol(E12.E, Decl(enumErrors.ts, 36, 11))
94+
>E : Symbol(E12.E, Decl(enumErrors.ts, 37, 11))
95+
96+
F = (() => 'foo')(),
97+
>F : Symbol(E12.F, Decl(enumErrors.ts, 38, 14))
9298
}
9399

94100
// Enum with incorrect syntax
95101
enum E13 {
96-
>E13 : Symbol(E13, Decl(enumErrors.ts, 38, 1))
102+
>E13 : Symbol(E13, Decl(enumErrors.ts, 40, 1))
97103

98104
postComma,
99-
>postComma : Symbol(E13.postComma, Decl(enumErrors.ts, 41, 10))
105+
>postComma : Symbol(E13.postComma, Decl(enumErrors.ts, 43, 10))
100106

101107
postValueComma = 1,
102-
>postValueComma : Symbol(E13.postValueComma, Decl(enumErrors.ts, 42, 14))
108+
>postValueComma : Symbol(E13.postValueComma, Decl(enumErrors.ts, 44, 14))
103109

104110
postSemicolon;
105-
>postSemicolon : Symbol(E13.postSemicolon, Decl(enumErrors.ts, 43, 23))
111+
>postSemicolon : Symbol(E13.postSemicolon, Decl(enumErrors.ts, 45, 23))
106112

107113
postColonValueComma: 2,
108-
>postColonValueComma : Symbol(E13.postColonValueComma, Decl(enumErrors.ts, 45, 18))
109-
>2 : Symbol(E13[2], Decl(enumErrors.ts, 46, 24))
114+
>postColonValueComma : Symbol(E13.postColonValueComma, Decl(enumErrors.ts, 47, 18))
115+
>2 : Symbol(E13[2], Decl(enumErrors.ts, 48, 24))
110116

111117
postColonValueSemicolon: 3;
112-
>postColonValueSemicolon : Symbol(E13.postColonValueSemicolon, Decl(enumErrors.ts, 46, 27))
113-
>3 : Symbol(E13[3], Decl(enumErrors.ts, 47, 28))
118+
>postColonValueSemicolon : Symbol(E13.postColonValueSemicolon, Decl(enumErrors.ts, 48, 27))
119+
>3 : Symbol(E13[3], Decl(enumErrors.ts, 49, 28))
114120

115121
};
116122

117123
enum E14 { a, b: any "hello" += 1, c, d}
118-
>E14 : Symbol(E14, Decl(enumErrors.ts, 48, 2))
119-
>a : Symbol(E14.a, Decl(enumErrors.ts, 50, 10))
120-
>b : Symbol(E14.b, Decl(enumErrors.ts, 50, 13))
121-
>any : Symbol(E14.any, Decl(enumErrors.ts, 50, 16))
122-
>"hello" : Symbol(E14["hello"], Decl(enumErrors.ts, 50, 20))
123-
>1 : Symbol(E14[1], Decl(enumErrors.ts, 50, 31))
124-
>c : Symbol(E14.c, Decl(enumErrors.ts, 50, 34))
125-
>d : Symbol(E14.d, Decl(enumErrors.ts, 50, 37))
124+
>E14 : Symbol(E14, Decl(enumErrors.ts, 50, 2))
125+
>a : Symbol(E14.a, Decl(enumErrors.ts, 52, 10))
126+
>b : Symbol(E14.b, Decl(enumErrors.ts, 52, 13))
127+
>any : Symbol(E14.any, Decl(enumErrors.ts, 52, 16))
128+
>"hello" : Symbol(E14["hello"], Decl(enumErrors.ts, 52, 20))
129+
>1 : Symbol(E14[1], Decl(enumErrors.ts, 52, 31))
130+
>c : Symbol(E14.c, Decl(enumErrors.ts, 52, 34))
131+
>d : Symbol(E14.d, Decl(enumErrors.ts, 52, 37))
126132

tests/baselines/reference/enumErrors.types

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,16 @@ enum E11 {
6969
>C : E11
7070
>window : Window & typeof globalThis
7171

72-
D = {}
72+
D = {},
7373
>D : E11
7474
>{} : {}
75+
76+
E = (() => 'foo')(),
77+
>E : E11
78+
>(() => 'foo')() : string
79+
>(() => 'foo') : () => string
80+
>() => 'foo' : () => string
81+
>'foo' : "foo"
7582
}
7683

7784
// Enum with string valued member and computed member initializers
@@ -100,6 +107,13 @@ enum E12 {
100107
>1 + 1 : number
101108
>1 : 1
102109
>1 : 1
110+
111+
F = (() => 'foo')(),
112+
>F : E12.B
113+
>(() => 'foo')() : string
114+
>(() => 'foo') : () => string
115+
>() => 'foo' : () => string
116+
>'foo' : "foo"
103117
}
104118

105119
// Enum with incorrect syntax

tests/cases/conformance/enums/enumErrors.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ enum E11 {
2626
A = true,
2727
B = new Date(),
2828
C = window,
29-
D = {}
29+
D = {},
30+
E = (() => 'foo')(),
3031
}
3132

3233
// Enum with string valued member and computed member initializers
@@ -36,6 +37,7 @@ enum E12 {
3637
C = window,
3738
D = {},
3839
E = 1 + 1,
40+
F = (() => 'foo')(),
3941
}
4042

4143
// Enum with incorrect syntax

0 commit comments

Comments
 (0)