diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a7d45e0ab8c..c1c994d9f3b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33676,7 +33676,13 @@ namespace ts { } else { // Only here do we need to check that the initializer is assignable to the enum type. - checkTypeAssignableTo(checkExpression(initializer), getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined); + const source = checkExpression(initializer); + if (!isTypeAssignableToKind(source, TypeFlags.NumberLike)) { + 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)); + } + else { + checkTypeAssignableTo(source, getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined); + } } return value; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 29bf61b2f75..ab45cbe0cf4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5737,5 +5737,9 @@ "The intersection '{0}' was reduced to 'never' because property '{1}' exists in multiple constituents and is private in some.": { "category": "Error", "code": 18032 + }, + "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.": { + "category": "Error", + "code": 18033 } } diff --git a/tests/baselines/reference/arrowFunctionContexts.errors.txt b/tests/baselines/reference/arrowFunctionContexts.errors.txt index b0d5d1dfc25..2a8fcbd2ca7 100644 --- a/tests/baselines/reference/arrowFunctionContexts.errors.txt +++ b/tests/baselines/reference/arrowFunctionContexts.errors.txt @@ -1,8 +1,8 @@ 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'. -tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(30,9): error TS2322: Type '() => number' is not assignable to type 'E'. +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. tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(31,16): error TS2332: 'this' cannot be referenced in current location. 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'. -tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(71,13): error TS2322: Type '() => number' is not assignable to type 'E'. +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. tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,20): error TS2332: 'this' cannot be referenced in current location. @@ -40,7 +40,7 @@ tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,20): e enum E { x = () => 4, // Error expected ~~~~~~~ -!!! error TS2322: Type '() => number' is not assignable to type 'E'. +!!! 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. y = (() => this).length // error, can't use this in enum ~~~~ !!! error TS2332: 'this' cannot be referenced in current location. @@ -87,7 +87,7 @@ tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,20): e enum E { x = () => 4, // Error expected ~~~~~~~ -!!! error TS2322: Type '() => number' is not assignable to type 'E'. +!!! 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. y = (() => this).length ~~~~ !!! error TS2332: 'this' cannot be referenced in current location. diff --git a/tests/baselines/reference/enumErrors.errors.txt b/tests/baselines/reference/enumErrors.errors.txt index 23b5e97e71b..ed2c1d2a3ed 100644 --- a/tests/baselines/reference/enumErrors.errors.txt +++ b/tests/baselines/reference/enumErrors.errors.txt @@ -2,28 +2,30 @@ tests/cases/conformance/enums/enumErrors.ts(2,6): error TS2431: Enum name cannot tests/cases/conformance/enums/enumErrors.ts(3,6): error TS2431: Enum name cannot be 'number'. tests/cases/conformance/enums/enumErrors.ts(4,6): error TS2431: Enum name cannot be 'string'. tests/cases/conformance/enums/enumErrors.ts(5,6): error TS2431: Enum name cannot be 'boolean'. -tests/cases/conformance/enums/enumErrors.ts(9,9): error TS2322: Type 'Number' is not assignable to type 'E5'. -tests/cases/conformance/enums/enumErrors.ts(26,9): error TS2322: Type 'true' is not assignable to type 'E11'. -tests/cases/conformance/enums/enumErrors.ts(27,9): error TS2322: Type 'Date' is not assignable to type 'E11'. -tests/cases/conformance/enums/enumErrors.ts(28,9): error TS2322: Type 'Window & typeof globalThis' is not assignable to type 'E11'. -tests/cases/conformance/enums/enumErrors.ts(29,9): error TS2322: Type '{}' is not assignable to type 'E11'. -tests/cases/conformance/enums/enumErrors.ts(35,9): error TS2553: Computed values are not permitted in an enum with string valued members. +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. +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. +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. +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. +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. +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. tests/cases/conformance/enums/enumErrors.ts(36,9): error TS2553: Computed values are not permitted in an enum with string valued members. tests/cases/conformance/enums/enumErrors.ts(37,9): error TS2553: Computed values are not permitted in an enum with string valued members. tests/cases/conformance/enums/enumErrors.ts(38,9): error TS2553: Computed values are not permitted in an enum with string valued members. -tests/cases/conformance/enums/enumErrors.ts(46,18): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(47,24): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(47,26): error TS2452: An enum member cannot have a numeric name. -tests/cases/conformance/enums/enumErrors.ts(48,28): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(48,30): error TS2452: An enum member cannot have a numeric name. -tests/cases/conformance/enums/enumErrors.ts(48,31): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(51,16): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(51,22): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(51,30): error TS1357: An enum member name must be followed by a ',', '=', or '}'. -tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member cannot have a numeric name. +tests/cases/conformance/enums/enumErrors.ts(39,9): error TS2553: Computed values are not permitted in an enum with string valued members. +tests/cases/conformance/enums/enumErrors.ts(40,9): error TS2553: Computed values are not permitted in an enum with string valued members. +tests/cases/conformance/enums/enumErrors.ts(48,18): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(49,24): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(49,26): error TS2452: An enum member cannot have a numeric name. +tests/cases/conformance/enums/enumErrors.ts(50,28): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(50,30): error TS2452: An enum member cannot have a numeric name. +tests/cases/conformance/enums/enumErrors.ts(50,31): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(53,16): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(53,22): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(53,30): error TS1357: An enum member name must be followed by a ',', '=', or '}'. +tests/cases/conformance/enums/enumErrors.ts(53,33): error TS2452: An enum member cannot have a numeric name. -==== tests/cases/conformance/enums/enumErrors.ts (23 errors) ==== +==== tests/cases/conformance/enums/enumErrors.ts (25 errors) ==== // Enum named with PredefinedTypes enum any { } ~~~ @@ -42,7 +44,7 @@ tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member enum E5 { C = new Number(30) ~~~~~~~~~~~~~~ -!!! error TS2322: Type 'Number' is not assignable to type 'E5'. +!!! 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. } enum E9 { @@ -61,16 +63,19 @@ tests/cases/conformance/enums/enumErrors.ts(51,33): error TS2452: An enum member enum E11 { A = true, ~~~~ -!!! error TS2322: Type 'true' is not assignable to type 'E11'. +!!! 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. B = new Date(), ~~~~~~~~~~ -!!! error TS2322: Type 'Date' is not assignable to type 'E11'. +!!! 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. C = window, ~~~~~~ -!!! error TS2322: Type 'Window & typeof globalThis' is not assignable to type 'E11'. - D = {} +!!! 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. + D = {}, ~~ -!!! error TS2322: Type '{}' is not assignable to type 'E11'. +!!! 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. + E = (() => 'foo')(), + ~~~~~~~~~~~~~~~ +!!! 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. } // 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 !!! error TS2553: Computed values are not permitted in an enum with string valued members. E = 1 + 1, ~~~~~ +!!! error TS2553: Computed values are not permitted in an enum with string valued members. + F = (() => 'foo')(), + ~~~~~~~~~~~~~~~ !!! error TS2553: Computed values are not permitted in an enum with string valued members. } diff --git a/tests/baselines/reference/enumErrors.js b/tests/baselines/reference/enumErrors.js index 64a884a0979..71c2393bd43 100644 --- a/tests/baselines/reference/enumErrors.js +++ b/tests/baselines/reference/enumErrors.js @@ -27,7 +27,8 @@ enum E11 { A = true, B = new Date(), C = window, - D = {} + D = {}, + E = (() => 'foo')(), } // Enum with string valued member and computed member initializers @@ -37,6 +38,7 @@ enum E12 { C = window, D = {}, E = 1 + 1, + F = (() => 'foo')(), } // Enum with incorrect syntax @@ -90,6 +92,7 @@ var E11; E11[E11["B"] = new Date()] = "B"; E11[E11["C"] = window] = "C"; E11[E11["D"] = {}] = "D"; + E11[E11["E"] = (function () { return 'foo'; })()] = "E"; })(E11 || (E11 = {})); // Enum with string valued member and computed member initializers var E12; @@ -99,6 +102,7 @@ var E12; E12[E12["C"] = 0] = "C"; E12[E12["D"] = 0] = "D"; E12[E12["E"] = 0] = "E"; + E12[E12["F"] = 0] = "F"; })(E12 || (E12 = {})); // Enum with incorrect syntax var E13; diff --git a/tests/baselines/reference/enumErrors.symbols b/tests/baselines/reference/enumErrors.symbols index d20fea828b1..0c10e392c90 100644 --- a/tests/baselines/reference/enumErrors.symbols +++ b/tests/baselines/reference/enumErrors.symbols @@ -65,62 +65,68 @@ enum E11 { >C : Symbol(E11.C, Decl(enumErrors.ts, 26, 19)) >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) - D = {} + D = {}, >D : Symbol(E11.D, Decl(enumErrors.ts, 27, 15)) + + E = (() => 'foo')(), +>E : Symbol(E11.E, Decl(enumErrors.ts, 28, 11)) } // Enum with string valued member and computed member initializers enum E12 { ->E12 : Symbol(E12, Decl(enumErrors.ts, 29, 1)) +>E12 : Symbol(E12, Decl(enumErrors.ts, 30, 1)) A = '', ->A : Symbol(E12.A, Decl(enumErrors.ts, 32, 10)) +>A : Symbol(E12.A, Decl(enumErrors.ts, 33, 10)) B = new Date(), ->B : Symbol(E12.B, Decl(enumErrors.ts, 33, 11)) +>B : Symbol(E12.B, Decl(enumErrors.ts, 34, 11)) >Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) C = window, ->C : Symbol(E12.C, Decl(enumErrors.ts, 34, 19)) +>C : Symbol(E12.C, Decl(enumErrors.ts, 35, 19)) >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) D = {}, ->D : Symbol(E12.D, Decl(enumErrors.ts, 35, 15)) +>D : Symbol(E12.D, Decl(enumErrors.ts, 36, 15)) E = 1 + 1, ->E : Symbol(E12.E, Decl(enumErrors.ts, 36, 11)) +>E : Symbol(E12.E, Decl(enumErrors.ts, 37, 11)) + + F = (() => 'foo')(), +>F : Symbol(E12.F, Decl(enumErrors.ts, 38, 14)) } // Enum with incorrect syntax enum E13 { ->E13 : Symbol(E13, Decl(enumErrors.ts, 38, 1)) +>E13 : Symbol(E13, Decl(enumErrors.ts, 40, 1)) postComma, ->postComma : Symbol(E13.postComma, Decl(enumErrors.ts, 41, 10)) +>postComma : Symbol(E13.postComma, Decl(enumErrors.ts, 43, 10)) postValueComma = 1, ->postValueComma : Symbol(E13.postValueComma, Decl(enumErrors.ts, 42, 14)) +>postValueComma : Symbol(E13.postValueComma, Decl(enumErrors.ts, 44, 14)) postSemicolon; ->postSemicolon : Symbol(E13.postSemicolon, Decl(enumErrors.ts, 43, 23)) +>postSemicolon : Symbol(E13.postSemicolon, Decl(enumErrors.ts, 45, 23)) postColonValueComma: 2, ->postColonValueComma : Symbol(E13.postColonValueComma, Decl(enumErrors.ts, 45, 18)) ->2 : Symbol(E13[2], Decl(enumErrors.ts, 46, 24)) +>postColonValueComma : Symbol(E13.postColonValueComma, Decl(enumErrors.ts, 47, 18)) +>2 : Symbol(E13[2], Decl(enumErrors.ts, 48, 24)) postColonValueSemicolon: 3; ->postColonValueSemicolon : Symbol(E13.postColonValueSemicolon, Decl(enumErrors.ts, 46, 27)) ->3 : Symbol(E13[3], Decl(enumErrors.ts, 47, 28)) +>postColonValueSemicolon : Symbol(E13.postColonValueSemicolon, Decl(enumErrors.ts, 48, 27)) +>3 : Symbol(E13[3], Decl(enumErrors.ts, 49, 28)) }; enum E14 { a, b: any "hello" += 1, c, d} ->E14 : Symbol(E14, Decl(enumErrors.ts, 48, 2)) ->a : Symbol(E14.a, Decl(enumErrors.ts, 50, 10)) ->b : Symbol(E14.b, Decl(enumErrors.ts, 50, 13)) ->any : Symbol(E14.any, Decl(enumErrors.ts, 50, 16)) ->"hello" : Symbol(E14["hello"], Decl(enumErrors.ts, 50, 20)) ->1 : Symbol(E14[1], Decl(enumErrors.ts, 50, 31)) ->c : Symbol(E14.c, Decl(enumErrors.ts, 50, 34)) ->d : Symbol(E14.d, Decl(enumErrors.ts, 50, 37)) +>E14 : Symbol(E14, Decl(enumErrors.ts, 50, 2)) +>a : Symbol(E14.a, Decl(enumErrors.ts, 52, 10)) +>b : Symbol(E14.b, Decl(enumErrors.ts, 52, 13)) +>any : Symbol(E14.any, Decl(enumErrors.ts, 52, 16)) +>"hello" : Symbol(E14["hello"], Decl(enumErrors.ts, 52, 20)) +>1 : Symbol(E14[1], Decl(enumErrors.ts, 52, 31)) +>c : Symbol(E14.c, Decl(enumErrors.ts, 52, 34)) +>d : Symbol(E14.d, Decl(enumErrors.ts, 52, 37)) diff --git a/tests/baselines/reference/enumErrors.types b/tests/baselines/reference/enumErrors.types index 24859e89b97..8a4884b5405 100644 --- a/tests/baselines/reference/enumErrors.types +++ b/tests/baselines/reference/enumErrors.types @@ -69,9 +69,16 @@ enum E11 { >C : E11 >window : Window & typeof globalThis - D = {} + D = {}, >D : E11 >{} : {} + + E = (() => 'foo')(), +>E : E11 +>(() => 'foo')() : string +>(() => 'foo') : () => string +>() => 'foo' : () => string +>'foo' : "foo" } // Enum with string valued member and computed member initializers @@ -100,6 +107,13 @@ enum E12 { >1 + 1 : number >1 : 1 >1 : 1 + + F = (() => 'foo')(), +>F : E12.B +>(() => 'foo')() : string +>(() => 'foo') : () => string +>() => 'foo' : () => string +>'foo' : "foo" } // Enum with incorrect syntax diff --git a/tests/cases/conformance/enums/enumErrors.ts b/tests/cases/conformance/enums/enumErrors.ts index 16360574167..cbb3168af18 100644 --- a/tests/cases/conformance/enums/enumErrors.ts +++ b/tests/cases/conformance/enums/enumErrors.ts @@ -26,7 +26,8 @@ enum E11 { A = true, B = new Date(), C = window, - D = {} + D = {}, + E = (() => 'foo')(), } // Enum with string valued member and computed member initializers @@ -36,6 +37,7 @@ enum E12 { C = window, D = {}, E = 1 + 1, + F = (() => 'foo')(), } // Enum with incorrect syntax