diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6d4b43549b7..3627d640943 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6152,6 +6152,19 @@ module ts { } } } + + if (type.flags & (TypeFlags.ObjectType | TypeFlags.Anonymous) && + type.symbol && + (type.symbol.flags & SymbolFlags.Enum) && + isConstEnumDeclaration(type.symbol.valueDeclaration)) { + // enum object type for const enums are only permitted in as 'left' in property access and 'object' in indexed access + var ok = + (node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).left === node) || + (node.parent.kind === SyntaxKind.IndexedAccess && (node.parent).object === node); + if (!ok) { + error(node, Diagnostics.const_enums_can_only_be_used_in_property_access_expressions); + } + } return type; } @@ -7590,6 +7603,7 @@ module ts { var enumType = getDeclaredTypeOfSymbol(enumSymbol); var autoValue = 0; var ambient = isInAmbientContext(node); + var enumIsConst = isConstEnumDeclaration(node); forEach(node.members, member => { if(isNumericName(member.name.text)) { @@ -7597,16 +7611,21 @@ module ts { } var initializer = member.initializer; if (initializer) { - autoValue = getConstantValueForEnumMemberInitializer(initializer); - if (autoValue === undefined && !ambient) { - // Only here do we need to check that the initializer is assignable to the enum type. - // If it is a constant value (not undefined), it is syntactically constrained to be a number. - // Also, we do not need to check this for ambients because there is already - // a syntax error if it is not a constant. - checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + autoValue = getConstantValueForEnumMemberInitializer(initializer, enumIsConst); + if (autoValue === undefined) { + if (enumIsConst) { + error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression); + } + else if (!ambient) { + // Only here do we need to check that the initializer is assignable to the enum type. + // If it is a constant value (not undefined), it is syntactically constrained to be a number. + // Also, we do not need to check this for ambients because there is already + // a syntax error if it is not a constant. + checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + } } } - else if (ambient) { + else if (ambient && !enumIsConst) { autoValue = undefined; } @@ -7618,7 +7637,7 @@ module ts { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } - function getConstantValueForEnumMemberInitializer(initializer: Expression): number { + function getConstantValueForEnumMemberInitializer(initializer: Expression, enumIsConst: boolean): number { return evalConstant(initializer); function evalConstant(e: Node): number { @@ -7631,11 +7650,11 @@ module ts { switch ((e).operator) { case SyntaxKind.PlusToken: return value; case SyntaxKind.MinusToken: return -value; - case SyntaxKind.TildeToken: return compilerOptions.propagateEnumConstants ? ~value : undefined; + case SyntaxKind.TildeToken: return enumIsConst ? ~value : undefined; } return undefined; case SyntaxKind.BinaryExpression: - if (!compilerOptions.propagateEnumConstants) { + if (!enumIsConst) { return undefined; } @@ -7655,14 +7674,20 @@ module ts { case SyntaxKind.GreaterThanGreaterThanToken: return left >> right; case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right; case SyntaxKind.LessThanLessThanToken: return left << right; + case SyntaxKind.AsteriskToken: return left * right; + case SyntaxKind.SlashToken: return left / right; + case SyntaxKind.PercentToken: return left % right; + case SyntaxKind.CaretToken: return left ^ right; } return undefined; case SyntaxKind.NumericLiteral: return +(e).text; + case SyntaxKind.ParenExpression: + return enumIsConst ? evalConstant((e).expression) : undefined; case SyntaxKind.Identifier: case SyntaxKind.IndexedAccess: case SyntaxKind.PropertyAccess: - if (!compilerOptions.propagateEnumConstants) { + if (!enumIsConst) { return undefined; } @@ -7738,6 +7763,21 @@ module ts { var enumSymbol = getSymbolOfNode(node); var firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind); if (node === firstDeclaration) { + if (enumSymbol.declarations.length > 1) { + var enumIsConst = isConstEnumDeclaration(node); + // check that const is places\omitted on all enum declarations + forEach(enumSymbol.declarations, decl => { + if (decl.kind !== SyntaxKind.EnumDeclaration) { + // TODO(vladima): do we want to allow merging for const enum declarations + return; + } + + if (isConstEnumDeclaration(decl) !== enumIsConst) { + error(decl.name, Diagnostics.Enum_declarations_must_all_be_const_or_non_const); + } + }); + } + var seenEnumMissingInitialInitializer = false; forEach(enumSymbol.declarations, declaration => { // return true if we hit a violation of the rule, false otherwise diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 667102ce315..dbc7498e9c4 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -118,11 +118,6 @@ module ts { shortName: "w", type: "boolean", description: Diagnostics.Watch_input_files, - }, - { - name: "propagateEnumConstants", - type: "boolean", - description: Diagnostics.Propagate_constant_values_in_enum_member_initializers } ]; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 6e6d4425d98..a6cc64f4497 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -353,6 +353,9 @@ module ts { Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 4076, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using name '{1}' from external module {2} but cannot be named." }, Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2: { code: 4077, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using name '{1}' from private module '{2}'." }, Parameter_0_of_exported_function_has_or_is_using_private_name_1: { code: 4078, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using private name '{1}'." }, + Enum_declarations_must_all_be_const_or_non_const: { code: 4079, category: DiagnosticCategory.Error, key: "Enum declarations must all be const or non-const." }, + In_const_enum_declarations_member_initializer_must_be_constant_expression: { code: 4079, category: DiagnosticCategory.Error, key: "In 'const' enum declarations member initializer must be constant expression." }, + const_enums_can_only_be_used_in_property_access_expressions: { code: 4079, category: DiagnosticCategory.Error, key: "'const' enums can only be used in property access expressions." }, The_current_host_does_not_support_the_0_option: { code: 5001, category: DiagnosticCategory.Error, key: "The current host does not support the '{0}' option." }, Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." }, Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" }, @@ -372,7 +375,6 @@ module ts { Specify_module_code_generation_Colon_commonjs_or_amd: { code: 6016, category: DiagnosticCategory.Message, key: "Specify module code generation: 'commonjs' or 'amd'" }, Print_this_message: { code: 6017, category: DiagnosticCategory.Message, key: "Print this message." }, Print_the_compiler_s_version: { code: 6019, category: DiagnosticCategory.Message, key: "Print the compiler's version." }, - Propagate_constant_values_in_enum_member_initializers: { code: 6020, category: DiagnosticCategory.Message, key: "Propagate constant values in enum member initializers." }, Syntax_Colon_0: { code: 6023, category: DiagnosticCategory.Message, key: "Syntax: {0}" }, options: { code: 6024, category: DiagnosticCategory.Message, key: "options" }, file: { code: 6025, category: DiagnosticCategory.Message, key: "file" }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index be0dced17dd..37cff2f273e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1409,13 +1409,22 @@ "category": "Error", "code": 4078 }, - - + "Enum declarations must all be const or non-const.": { + "category": "Error", + "code": 4079 + }, + "In 'const' enum declarations member initializer must be constant expression.": { + "category": "Error", + "code": 4079 + }, + "'const' enums can only be used in property access expressions.": { + "category": "Error", + "code": 4079 + }, "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 }, - "Cannot find the common subdirectory path for the input files.": { "category": "Error", "code": 5009 @@ -1488,10 +1497,6 @@ "category": "Message", "code": 6019 }, - "Propagate constant values in enum member initializers.": { - "category": "Message", - "code": 6020 - }, "Syntax: {0}": { "category": "Message", "code": 6023 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1517584e4c8..0cf81dc46ea 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1760,6 +1760,9 @@ module ts { } function emitEnumDeclaration(node: EnumDeclaration) { + if (isConstEnumDeclaration(node)) { + return; + } emitLeadingComments(node); if (!(node.flags & NodeFlags.Export)) { emitStart(node); @@ -2570,6 +2573,9 @@ module ts { if (resolver.isDeclarationVisible(node)) { emitJsDocComments(node); emitDeclarationFlags(node); + if (isConstEnumDeclaration(node)) { + write("const ") + } write("enum "); emitSourceTextOfNode(node.name); write(" {"); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 15383a0031b..b21258fb9e6 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -186,6 +186,10 @@ module ts { return (file.flags & NodeFlags.DeclarationFile) !== 0; } + export function isConstEnumDeclaration(node: EnumDeclaration): boolean { + return (node.flags & NodeFlags.Const) !== 0; + } + export function isPrologueDirective(node: Node): boolean { return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral; } @@ -3146,7 +3150,6 @@ module ts { case SyntaxKind.OpenBraceToken: case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: - case SyntaxKind.ConstKeyword: case SyntaxKind.FunctionKeyword: case SyntaxKind.IfKeyword: case SyntaxKind.DoKeyword: @@ -3165,6 +3168,12 @@ module ts { case SyntaxKind.CatchKeyword: case SyntaxKind.FinallyKeyword: return true; + case SyntaxKind.ConstKeyword: + // const keyword can precede enum keyword when defining constant enums + // 'const enum' do not start statement. + // In ES 6 'enum' is a future reserved keyword, so it should not be used as identifier + var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); + return !isConstEnum; case SyntaxKind.InterfaceKeyword: case SyntaxKind.ClassKeyword: case SyntaxKind.ModuleKeyword: @@ -3174,6 +3183,7 @@ module ts { if (isDeclaration()) { return false; } + case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: @@ -3195,6 +3205,7 @@ module ts { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: + // const here should always be parsed as const declaration because of check in 'isStatement' return parseVariableStatement(allowLetAndConstDeclarations); case SyntaxKind.FunctionKeyword: return parseFunctionDeclaration(); @@ -3748,6 +3759,7 @@ module ts { } function parseAndCheckEnumDeclaration(pos: number, flags: NodeFlags): EnumDeclaration { + var enumIsConst = flags & NodeFlags.Const; function isIntegerLiteral(expression: Expression): boolean { function isInteger(literalExpression: LiteralExpression): boolean { // Allows for scientific notation since literalExpression.text was formed by @@ -3782,22 +3794,29 @@ module ts { node.name = parsePropertyName(); node.initializer = parseInitializer(/*inParameter*/ false); - if (inAmbientContext) { - if (node.initializer && !isIntegerLiteral(node.initializer) && errorCountBeforeEnumMember === file.syntacticErrors.length) { - grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers); + // skip checks below for const enums - they allow arbitrary initializers as long as they can be evaluated to constant expressions. + // since all values are known in compile time - it is not necessary to check that constant enum section precedes computed enum members. + if (!enumIsConst) { + if (inAmbientContext) { + if (node.initializer && !isIntegerLiteral(node.initializer) && errorCountBeforeEnumMember === file.syntacticErrors.length) { + grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers); + } + } + else if (node.initializer) { + inConstantEnumMemberSection = isIntegerLiteral(node.initializer); + } + else if (!inConstantEnumMemberSection && errorCountBeforeEnumMember === file.syntacticErrors.length) { + grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer); } - } - else if (node.initializer) { - inConstantEnumMemberSection = isIntegerLiteral(node.initializer); - } - else if (!inConstantEnumMemberSection && errorCountBeforeEnumMember === file.syntacticErrors.length) { - grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer); } return finishNode(node); } var node = createNode(SyntaxKind.EnumDeclaration, pos); node.flags = flags; + if (enumIsConst) { + parseExpected(SyntaxKind.ConstKeyword); + } parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -3953,9 +3972,17 @@ module ts { switch (token) { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: - case SyntaxKind.ConstKeyword: result = parseVariableStatement(/*allowLetAndConstDeclarations*/ true, pos, flags); break; + case SyntaxKind.ConstKeyword: + var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); + if (isConstEnum) { + result = parseAndCheckEnumDeclaration(pos, flags | NodeFlags.Const); + } + else { + result = parseVariableStatement(/*allowLetAndConstDeclarations*/ true, pos, flags); + } + break; case SyntaxKind.FunctionKeyword: result = parseFunctionDeclaration(pos, flags); break; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fca019485c7..d28e6d67e5d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1095,7 +1095,6 @@ module ts { target?: ScriptTarget; version?: boolean; watch?: boolean; - propagateEnumConstants?: boolean; [option: string]: any; } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f537541b5b3..2e837c7887d 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -749,9 +749,6 @@ module Harness { case 'usecasesensitivefilenames': useCaseSensitiveFileNames = setting.value === 'true'; break; - case 'propagateenumconstants': - options.propagateEnumConstants = setting.value === 'true'; - break; case 'mapsourcefiles': case 'maproot': @@ -1148,7 +1145,7 @@ module Harness { var optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*(\S*)/gm; // multiple matches on multiple lines // List of allowed metadata names - var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames", "propagateenumconstants"]; + var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames"]; function extractCompilerSettings(content: string): CompilerSetting[] { diff --git a/tests/baselines/reference/constantsInEnumMembers.errors.txt b/tests/baselines/reference/constantsInEnumMembers.errors.txt new file mode 100644 index 00000000000..323a352ebb2 --- /dev/null +++ b/tests/baselines/reference/constantsInEnumMembers.errors.txt @@ -0,0 +1,122 @@ +tests/cases/compiler/constantsInEnumMembers.ts(38,9): error TS4079: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constantsInEnumMembers.ts(40,9): error TS4079: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constantsInEnumMembers.ts(41,10): error TS4079: In 'const' enum declarations member initializer must be constant expression. + + +==== tests/cases/compiler/constantsInEnumMembers.ts (3 errors) ==== + const enum Enum1 { + A0 = 100, + } + + const enum Enum1 { + // correct cases + A, + B, + C = 10, + D = A + B, + E = A + 1, + F = 1 + A, + G = 1 + 1, + H = A - B, + I = A - 1, + J = 1 - A, + K = 1 - 1, + L = ~D, + M = E << B, + N = E << 1, + O = E >> B, + P = E >> 1, + Q = -D, + R = C & 5, + S = 5 & C, + T = C | D, + U = C | 1, + V = 10 | D, + W = Enum1.V, + + // correct cases: reference to the enum member from different enum declaration + W1 = A0, + W2 = Enum1.A0, + W3 = Enum1["A0"], + W4 = Enum1["W"], + // illegal case + // forward reference to the element of the same enum + X = Y, + ~ +!!! error TS4079: In 'const' enum declarations member initializer must be constant expression. + // forward reference to the element of the same enum + Y = Enum1.Z, + ~~~~~~~ +!!! error TS4079: In 'const' enum declarations member initializer must be constant expression. + Y1 = Enum1["Z"], + ~~~~~~~~~~ +!!! error TS4079: In 'const' enum declarations member initializer must be constant expression. + Z = 100, + } + + + module A { + export module B { + export module C { + export const enum E { + V1 = 1, + V2 = A.B.C.E.V1 + 100 + } + } + } + } + + module A { + export module B { + export module C { + export const enum E { + V3 = A.B.C.E["V2"] + 200, + } + } + } + } + + function foo(x: Enum1) { + switch (x) { + case Enum1.A: + case Enum1.B: + case Enum1.C: + case Enum1.D: + case Enum1.E: + case Enum1.F: + case Enum1.G: + case Enum1.H: + case Enum1.I: + case Enum1.J: + case Enum1.K: + case Enum1.L: + case Enum1.M: + case Enum1.N: + case Enum1.O: + case Enum1.P: + case Enum1.Q: + case Enum1.R: + case Enum1.S: + case Enum1.T: + case Enum1.U: + case Enum1.V: + case Enum1.W: + case Enum1.W1: + case Enum1.W2: + case Enum1.W3: + case Enum1.W4: + case Enum1.X: + case Enum1.Y: + case Enum1.Y1: + case Enum1.Z: + break; + } + } + + function bar(e: A.B.C.E): number { + switch (e) { + case A.B.C.E.V1: return 1; + case A.B.C.E.V2: return 1; + case A.B.C.E.V3: return 1; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/constantsInEnumMembers.js b/tests/baselines/reference/constantsInEnumMembers.js index c4bd95b46d5..62d6f9fbf3c 100644 --- a/tests/baselines/reference/constantsInEnumMembers.js +++ b/tests/baselines/reference/constantsInEnumMembers.js @@ -1,10 +1,9 @@ //// [constantsInEnumMembers.ts] - -enum Enum1 { +const enum Enum1 { A0 = 100, } -enum Enum1 { +const enum Enum1 { // correct cases A, B, @@ -48,7 +47,7 @@ enum Enum1 { module A { export module B { export module C { - export enum E { + export const enum E { V1 = 1, V2 = A.B.C.E.V1 + 100 } @@ -59,7 +58,7 @@ module A { module A { export module B { export module C { - export enum E { + export const enum E { V3 = A.B.C.E["V2"] + 200, } } @@ -112,60 +111,12 @@ function bar(e: A.B.C.E): number { } //// [constantsInEnumMembers.js] -var Enum1; -(function (Enum1) { - Enum1[Enum1["A0"] = 100] = "A0"; -})(Enum1 || (Enum1 = {})); -var Enum1; -(function (Enum1) { - // correct cases - Enum1[Enum1["A"] = 0] = "A"; - Enum1[Enum1["B"] = 1] = "B"; - Enum1[Enum1["C"] = 10] = "C"; - Enum1[Enum1["D"] = A + B] = "D"; - Enum1[Enum1["E"] = A + 1] = "E"; - Enum1[Enum1["F"] = 1 + A] = "F"; - Enum1[Enum1["G"] = 1 + 1] = "G"; - Enum1[Enum1["H"] = A - B] = "H"; - Enum1[Enum1["I"] = A - 1] = "I"; - Enum1[Enum1["J"] = 1 - A] = "J"; - Enum1[Enum1["K"] = 1 - 1] = "K"; - Enum1[Enum1["L"] = ~D] = "L"; - Enum1[Enum1["M"] = E << B] = "M"; - Enum1[Enum1["N"] = E << 1] = "N"; - Enum1[Enum1["O"] = E >> B] = "O"; - Enum1[Enum1["P"] = E >> 1] = "P"; - Enum1[Enum1["Q"] = -D] = "Q"; - Enum1[Enum1["R"] = C & 5] = "R"; - Enum1[Enum1["S"] = 5 & C] = "S"; - Enum1[Enum1["T"] = C | D] = "T"; - Enum1[Enum1["U"] = C | 1] = "U"; - Enum1[Enum1["V"] = 10 | D] = "V"; - Enum1[Enum1["W"] = Enum1.V] = "W"; - // correct cases: reference to the enum member from different enum declaration - Enum1[Enum1["W1"] = A0] = "W1"; - Enum1[Enum1["W2"] = Enum1.A0] = "W2"; - Enum1[Enum1["W3"] = Enum1["A0"]] = "W3"; - Enum1[Enum1["W4"] = Enum1["W"]] = "W4"; - // illegal case - // forward reference to the element of the same enum - Enum1[Enum1["X"] = Enum1.Y] = "X"; - // forward reference to the element of the same enum - Enum1[Enum1["Y"] = 100 /* Z */] = "Y"; - Enum1[Enum1["Y1"] = Enum1["Z"]] = "Y1"; - Enum1[Enum1["Z"] = 100] = "Z"; -})(Enum1 || (Enum1 = {})); var A; (function (A) { var B; (function (B) { var C; (function (C) { - (function (E) { - E[E["V1"] = 1] = "V1"; - E[E["V2"] = A.B.C.E.V1 + 100] = "V2"; - })(C.E || (C.E = {})); - var E = C.E; })(C = B.C || (B.C = {})); })(B = A.B || (A.B = {})); })(A || (A = {})); @@ -175,10 +126,6 @@ var A; (function (B) { var C; (function (C) { - (function (E) { - E[E["V3"] = A.B.C.E["V2"] + 200] = "V3"; - })(C.E || (C.E = {})); - var E = C.E; })(C = B.C || (B.C = {})); })(B = A.B || (A.B = {})); })(A || (A = {})); diff --git a/tests/baselines/reference/constantsInEnumMembers.types b/tests/baselines/reference/constantsInEnumMembers.types deleted file mode 100644 index 52a1070ebe4..00000000000 --- a/tests/baselines/reference/constantsInEnumMembers.types +++ /dev/null @@ -1,442 +0,0 @@ -=== tests/cases/compiler/constantsInEnumMembers.ts === - -enum Enum1 { ->Enum1 : Enum1 - - A0 = 100, ->A0 : Enum1 -} - -enum Enum1 { ->Enum1 : Enum1 - - // correct cases - A, ->A : Enum1 - - B, ->B : Enum1 - - C = 10, ->C : Enum1 - - D = A + B, ->D : Enum1 ->A + B : number ->A : Enum1 ->B : Enum1 - - E = A + 1, ->E : Enum1 ->A + 1 : number ->A : Enum1 - - F = 1 + A, ->F : Enum1 ->1 + A : number ->A : Enum1 - - G = 1 + 1, ->G : Enum1 ->1 + 1 : number - - H = A - B, ->H : Enum1 ->A - B : number ->A : Enum1 ->B : Enum1 - - I = A - 1, ->I : Enum1 ->A - 1 : number ->A : Enum1 - - J = 1 - A, ->J : Enum1 ->1 - A : number ->A : Enum1 - - K = 1 - 1, ->K : Enum1 ->1 - 1 : number - - L = ~D, ->L : Enum1 ->~D : number ->D : Enum1 - - M = E << B, ->M : Enum1 ->E << B : number ->E : Enum1 ->B : Enum1 - - N = E << 1, ->N : Enum1 ->E << 1 : number ->E : Enum1 - - O = E >> B, ->O : Enum1 ->E >> B : number ->E : Enum1 ->B : Enum1 - - P = E >> 1, ->P : Enum1 ->E >> 1 : number ->E : Enum1 - - Q = -D, ->Q : Enum1 ->-D : number ->D : Enum1 - - R = C & 5, ->R : Enum1 ->C & 5 : number ->C : Enum1 - - S = 5 & C, ->S : Enum1 ->5 & C : number ->C : Enum1 - - T = C | D, ->T : Enum1 ->C | D : number ->C : Enum1 ->D : Enum1 - - U = C | 1, ->U : Enum1 ->C | 1 : number ->C : Enum1 - - V = 10 | D, ->V : Enum1 ->10 | D : number ->D : Enum1 - - W = Enum1.V, ->W : Enum1 ->Enum1.V : Enum1 ->Enum1 : typeof Enum1 ->V : Enum1 - - // correct cases: reference to the enum member from different enum declaration - W1 = A0, ->W1 : Enum1 ->A0 : Enum1 - - W2 = Enum1.A0, ->W2 : Enum1 ->Enum1.A0 : Enum1 ->Enum1 : typeof Enum1 ->A0 : Enum1 - - W3 = Enum1["A0"], ->W3 : Enum1 ->Enum1["A0"] : Enum1 ->Enum1 : typeof Enum1 - - W4 = Enum1["W"], ->W4 : Enum1 ->Enum1["W"] : Enum1 ->Enum1 : typeof Enum1 - - // illegal case - // forward reference to the element of the same enum - X = Y, ->X : Enum1 ->Y : Enum1 - - // forward reference to the element of the same enum - Y = Enum1.Z, ->Y : Enum1 ->Enum1.Z : Enum1 ->Enum1 : typeof Enum1 ->Z : Enum1 - - Y1 = Enum1["Z"], ->Y1 : Enum1 ->Enum1["Z"] : Enum1 ->Enum1 : typeof Enum1 - - Z = 100, ->Z : Enum1 -} - - -module A { ->A : typeof A - - export module B { ->B : typeof B - - export module C { ->C : typeof C - - export enum E { ->E : E - - V1 = 1, ->V1 : E - - V2 = A.B.C.E.V1 + 100 ->V2 : E ->A.B.C.E.V1 + 100 : number ->A.B.C.E.V1 : E ->A.B.C.E : typeof E ->A.B.C : typeof C ->A.B : typeof B ->A : typeof A ->B : typeof B ->C : typeof C ->E : typeof E ->V1 : E - } - } - } -} - -module A { ->A : typeof A - - export module B { ->B : typeof B - - export module C { ->C : typeof C - - export enum E { ->E : E - - V3 = A.B.C.E["V2"] + 200, ->V3 : E ->A.B.C.E["V2"] + 200 : number ->A.B.C.E["V2"] : E ->A.B.C.E : typeof E ->A.B.C : typeof C ->A.B : typeof B ->A : typeof A ->B : typeof B ->C : typeof C ->E : typeof E - } - } - } -} - -function foo(x: Enum1) { ->foo : (x: Enum1) => void ->x : Enum1 ->Enum1 : Enum1 - - switch (x) { ->x : Enum1 - - case Enum1.A: ->Enum1.A : Enum1 ->Enum1 : typeof Enum1 ->A : Enum1 - - case Enum1.B: ->Enum1.B : Enum1 ->Enum1 : typeof Enum1 ->B : Enum1 - - case Enum1.C: ->Enum1.C : Enum1 ->Enum1 : typeof Enum1 ->C : Enum1 - - case Enum1.D: ->Enum1.D : Enum1 ->Enum1 : typeof Enum1 ->D : Enum1 - - case Enum1.E: ->Enum1.E : Enum1 ->Enum1 : typeof Enum1 ->E : Enum1 - - case Enum1.F: ->Enum1.F : Enum1 ->Enum1 : typeof Enum1 ->F : Enum1 - - case Enum1.G: ->Enum1.G : Enum1 ->Enum1 : typeof Enum1 ->G : Enum1 - - case Enum1.H: ->Enum1.H : Enum1 ->Enum1 : typeof Enum1 ->H : Enum1 - - case Enum1.I: ->Enum1.I : Enum1 ->Enum1 : typeof Enum1 ->I : Enum1 - - case Enum1.J: ->Enum1.J : Enum1 ->Enum1 : typeof Enum1 ->J : Enum1 - - case Enum1.K: ->Enum1.K : Enum1 ->Enum1 : typeof Enum1 ->K : Enum1 - - case Enum1.L: ->Enum1.L : Enum1 ->Enum1 : typeof Enum1 ->L : Enum1 - - case Enum1.M: ->Enum1.M : Enum1 ->Enum1 : typeof Enum1 ->M : Enum1 - - case Enum1.N: ->Enum1.N : Enum1 ->Enum1 : typeof Enum1 ->N : Enum1 - - case Enum1.O: ->Enum1.O : Enum1 ->Enum1 : typeof Enum1 ->O : Enum1 - - case Enum1.P: ->Enum1.P : Enum1 ->Enum1 : typeof Enum1 ->P : Enum1 - - case Enum1.Q: ->Enum1.Q : Enum1 ->Enum1 : typeof Enum1 ->Q : Enum1 - - case Enum1.R: ->Enum1.R : Enum1 ->Enum1 : typeof Enum1 ->R : Enum1 - - case Enum1.S: ->Enum1.S : Enum1 ->Enum1 : typeof Enum1 ->S : Enum1 - - case Enum1.T: ->Enum1.T : Enum1 ->Enum1 : typeof Enum1 ->T : Enum1 - - case Enum1.U: ->Enum1.U : Enum1 ->Enum1 : typeof Enum1 ->U : Enum1 - - case Enum1.V: ->Enum1.V : Enum1 ->Enum1 : typeof Enum1 ->V : Enum1 - - case Enum1.W: ->Enum1.W : Enum1 ->Enum1 : typeof Enum1 ->W : Enum1 - - case Enum1.W1: ->Enum1.W1 : Enum1 ->Enum1 : typeof Enum1 ->W1 : Enum1 - - case Enum1.W2: ->Enum1.W2 : Enum1 ->Enum1 : typeof Enum1 ->W2 : Enum1 - - case Enum1.W3: ->Enum1.W3 : Enum1 ->Enum1 : typeof Enum1 ->W3 : Enum1 - - case Enum1.W4: ->Enum1.W4 : Enum1 ->Enum1 : typeof Enum1 ->W4 : Enum1 - - case Enum1.X: ->Enum1.X : Enum1 ->Enum1 : typeof Enum1 ->X : Enum1 - - case Enum1.Y: ->Enum1.Y : Enum1 ->Enum1 : typeof Enum1 ->Y : Enum1 - - case Enum1.Y1: ->Enum1.Y1 : Enum1 ->Enum1 : typeof Enum1 ->Y1 : Enum1 - - case Enum1.Z: ->Enum1.Z : Enum1 ->Enum1 : typeof Enum1 ->Z : Enum1 - - break; - } -} - -function bar(e: A.B.C.E): number { ->bar : (e: A.B.C.E) => number ->e : A.B.C.E ->A : unknown ->B : unknown ->C : unknown ->E : A.B.C.E - - switch (e) { ->e : A.B.C.E - - case A.B.C.E.V1: return 1; ->A.B.C.E.V1 : A.B.C.E ->A.B.C.E : typeof A.B.C.E ->A.B.C : typeof A.B.C ->A.B : typeof A.B ->A : typeof A ->B : typeof A.B ->C : typeof A.B.C ->E : typeof A.B.C.E ->V1 : A.B.C.E - - case A.B.C.E.V2: return 1; ->A.B.C.E.V2 : A.B.C.E ->A.B.C.E : typeof A.B.C.E ->A.B.C : typeof A.B.C ->A.B : typeof A.B ->A : typeof A ->B : typeof A.B ->C : typeof A.B.C ->E : typeof A.B.C.E ->V2 : A.B.C.E - - case A.B.C.E.V3: return 1; ->A.B.C.E.V3 : A.B.C.E ->A.B.C.E : typeof A.B.C.E ->A.B.C : typeof A.B.C ->A.B : typeof A.B ->A : typeof A ->B : typeof A.B ->C : typeof A.B.C ->E : typeof A.B.C.E ->V3 : A.B.C.E - } -} diff --git a/tests/cases/compiler/constantsInEnumMembers.ts b/tests/cases/compiler/constantsInEnumMembers.ts index 8f3e5bd39e2..dfaac3ffe0a 100644 --- a/tests/cases/compiler/constantsInEnumMembers.ts +++ b/tests/cases/compiler/constantsInEnumMembers.ts @@ -1,10 +1,8 @@ -// @propagateEnumConstants: true - -enum Enum1 { +const enum Enum1 { A0 = 100, } -enum Enum1 { +const enum Enum1 { // correct cases A, B, @@ -48,7 +46,7 @@ enum Enum1 { module A { export module B { export module C { - export enum E { + export const enum E { V1 = 1, V2 = A.B.C.E.V1 + 100 } @@ -59,7 +57,7 @@ module A { module A { export module B { export module C { - export enum E { + export const enum E { V3 = A.B.C.E["V2"] + 200, } }