diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f0b2d71ccad..81404c933ef 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -5,21 +5,25 @@ module ts { - export function isInstantiated(node: Node): boolean { + export function isInstantiated(node: Node, checkConstEnums: boolean): boolean { // A module is uninstantiated if it contains only // 1. interface declarations if (node.kind === SyntaxKind.InterfaceDeclaration) { return false; } - // 2. non - exported import declarations + // 2. const enum declarations don't make module instantiated + else if (checkConstEnums && node.kind === SyntaxKind.EnumDeclaration && isConstEnumDeclaration(node)) { + return false; + } + // 3. non - exported import declarations else if (node.kind === SyntaxKind.ImportDeclaration && !(node.flags & NodeFlags.Export)) { return false; } - // 3. other uninstantiated module declarations. - else if (node.kind === SyntaxKind.ModuleBlock && !forEachChild(node, isInstantiated)) { + // 4. other uninstantiated module declarations. + else if (node.kind === SyntaxKind.ModuleBlock && !forEachChild(node, n => isInstantiated(n, checkConstEnums))) { return false; } - else if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated((node).body)) { + else if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated((node).body, checkConstEnums)) { return false; } else { @@ -248,7 +252,7 @@ module ts { if (node.name.kind === SyntaxKind.StringLiteral) { bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); } - else if (isInstantiated(node)) { + else if (isInstantiated(node, /*checkConstEnums*/ false)) { bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); } else { @@ -364,7 +368,10 @@ module ts { bindDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.EnumDeclaration: - bindDeclaration(node, SymbolFlags.Enum, SymbolFlags.EnumExcludes, /*isBlockScopeContainer*/ false); + var enumIsConst = isConstEnumDeclaration(node); + var flags = enumIsConst ? SymbolFlags.Enum | SymbolFlags.ConstEnum : SymbolFlags.Enum; + var excludes = enumIsConst ? SymbolFlags.ConstEnumExcludes : SymbolFlags.EnumExcludes; + bindDeclaration(node, flags, excludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.ModuleDeclaration: bindModuleDeclaration(node); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 27bb6548b90..02757e458c8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4431,8 +4431,8 @@ module ts { if (symbol.flags & SymbolFlags.Import) { // Mark the import as referenced so that we emit it in the final .js file. - // exception: identifiers that appear in type queries - getSymbolLinks(symbol).referenced = !isInTypeQuery(node); + // exception: identifiers that appear in type queries, const enums + getSymbolLinks(symbol).referenced = !isInTypeQuery(node) && !isConstEnumSymbol(resolveImport(symbol)); } checkCollisionWithCapturedSuperVariable(node, node); @@ -5086,6 +5086,10 @@ module ts { if (objectType === unknownType) return unknownType; + if (isConstEnumType(objectType) && node.index.kind !== SyntaxKind.StringLiteral) { + error(node.index, Diagnostics.Index_expression_arguments_in_const_enums_must_be_of_type_string); + } + // TypeScript 1.0 spec (April 2014): 4.10 Property Access // - If IndexExpr is a string literal or a numeric literal and ObjExpr's apparent type has a property with the name // given by that literal(converted to its string representation in the case of a numeric literal), the property access is of the type of that property. @@ -5962,6 +5966,14 @@ module ts { return (type.flags & TypeFlags.Structured) !== 0; } + function isConstEnumType(type: Type) : boolean { + return type.flags & (TypeFlags.ObjectType | TypeFlags.Anonymous) && type.symbol && isConstEnumSymbol(type.symbol); + } + + function isConstEnumSymbol(symbol: Symbol): boolean { + return (symbol.flags & SymbolFlags.ConstEnum) !== 0; + } + function checkInstanceOfExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type { // TypeScript 1.0 spec (April 2014): 4.15.4 // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, @@ -6187,19 +6199,31 @@ 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 + if (isConstEnumType(type)) { + // enum object type for const enums are only permitted in: + // - 'left' in property access + // - 'object' in indexed access + // - target in rhs of import statement var ok = (node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).left === node) || - (node.parent.kind === SyntaxKind.IndexedAccess && (node.parent).object === node); + (node.parent.kind === SyntaxKind.IndexedAccess && (node.parent).object === node) || + isRhsOfImportStatement(node); + if (!ok) { error(node, Diagnostics.const_enums_can_only_be_used_in_property_access_expressions); } } return type; + + function isRhsOfImportStatement(n: Node): boolean { + while (n.parent) { + if (n.parent.kind === SyntaxKind.ImportDeclaration && (n.parent).entityName === n) { + return true; + } + n = n.parent; + } + return false; + } } function checkExpressionNode(node: Expression, contextualMapper: TypeMapper): Type { @@ -6868,7 +6892,7 @@ module ts { case SyntaxKind.InterfaceDeclaration: return SymbolFlags.ExportType; case SyntaxKind.ModuleDeclaration: - return (d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d) + return (d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d, /*checkConstEnums*/ false) ? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue : SymbolFlags.ExportNamespace; case SyntaxKind.ClassDeclaration: @@ -7105,7 +7129,7 @@ module ts { } // Uninstantiated modules shouldnt do this check - if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated(node)) { + if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated(node, /*checkConstEnums*/ true)) { return; } @@ -7707,14 +7731,9 @@ module ts { switch ((e).operator) { case SyntaxKind.BarToken: return left | right; case SyntaxKind.AmpersandToken: return left & right; - case SyntaxKind.PlusToken: return left + right; - case SyntaxKind.MinusToken: return left - right; 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; @@ -7803,13 +7822,8 @@ module ts { if (node === firstDeclaration) { if (enumSymbol.declarations.length > 1) { var enumIsConst = isConstEnumDeclaration(node); - // check that const is places\omitted on all enum declarations + // check that const is placed\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); } @@ -8655,7 +8669,7 @@ module ts { } var symbol = getSymbolOfNode(node); var target = resolveImport(symbol); - return target !== unknownSymbol && ((target.flags & SymbolFlags.Value) !== 0); + return target !== unknownSymbol && ((target.flags & SymbolFlags.Value) !== 0) && !isConstEnumSymbol(target); } function hasSemanticErrors() { @@ -8676,7 +8690,8 @@ module ts { // As a consequence this might cause emitting extra. if (node.flags & NodeFlags.Export) { var target = resolveImport(symbol); - if (target !== unknownSymbol && target.flags & SymbolFlags.Value) { + // importing const enum does not cause import to be referenced + if (target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumSymbol(target)) { return true; } } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4ee747c0c0b..47520cb0317 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -118,6 +118,10 @@ module ts { shortName: "w", type: "boolean", description: Diagnostics.Watch_input_files, + }, + { + name: "preserveConstEnums", + type: "boolean" } ]; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 211531e2fa0..8bf05e403c8 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -353,6 +353,7 @@ module ts { Enum_declarations_must_all_be_const_or_non_const: { code: 4082, category: DiagnosticCategory.Error, key: "Enum declarations must all be const or non-const." }, In_const_enum_declarations_member_initializer_must_be_constant_expression: { code: 4083, 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: 4084, category: DiagnosticCategory.Error, key: "'const' enums can only be used in property access expressions." }, + Index_expression_arguments_in_const_enums_must_be_of_type_string: { code: 4085, category: DiagnosticCategory.Error, key: "Index expression arguments in 'const' enums must be of type 'string'." }, 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}" }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 713fa097980..9b9094223ce 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1412,7 +1412,11 @@ "'const' enums can only be used in property access expressions.": { "category": "Error", "code": 4084 - }, + }, + "Index expression arguments in 'const' enums must be of type 'string'.": { + "category": "Error", + "code": 4085 + }, "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 908505025ab..42992107eaa 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1760,7 +1760,8 @@ module ts { } function emitEnumDeclaration(node: EnumDeclaration) { - if (isConstEnumDeclaration(node)) { + // const enums are completely erased during compilation. + if (isConstEnumDeclaration(node) && !compilerOptions.preserveConstEnums) { return; } emitLeadingComments(node); @@ -1837,7 +1838,7 @@ module ts { } function emitModuleDeclaration(node: ModuleDeclaration) { - if (!isInstantiated(node)) { + if (!isInstantiated(node, /*checkConstEnums*/ true)) { return emitPinnedOrTripleSlashComments(node); } emitLeadingComments(node); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6a51691f1ab..055ae8b3e77 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -783,6 +783,7 @@ module ts { Transient = 0x08000000, // Transient symbol (created during type check) Prototype = 0x10000000, // Prototype property (no source representation) UnionProperty = 0x20000000, // Property in union type + ConstEnum = 0x40000000, // Const enum marker Variable = FunctionScopedVariable | BlockScopedVariable, Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor, @@ -807,7 +808,8 @@ module ts { ClassExcludes = (Value | Type) & ~ValueModule, InterfaceExcludes = Type & ~Interface, EnumExcludes = (Value | Type) & ~(Enum | ValueModule), - ValueModuleExcludes = Value & ~(Function | Class | Enum | ValueModule), + ConstEnumExcludes = (Value | Type) & ~Enum, // const enums merge only with enums + ValueModuleExcludes = (Value | ConstEnum) & ~(Function | Class | Enum | ValueModule), NamespaceModuleExcludes = 0, MethodExcludes = Value & ~Method, GetAccessorExcludes = Value & ~SetAccessor, @@ -1076,6 +1078,7 @@ module ts { target?: ScriptTarget; version?: boolean; watch?: boolean; + preserveConstEnums?: boolean; [option: string]: string | number | boolean; } diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 3a72ee9bfa6..319bb12d894 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -178,7 +178,7 @@ module ts.BreakpointResolver { case SyntaxKind.ModuleDeclaration: // span on complete module if it is instantiated - if (!isInstantiated(node)) { + if (!isInstantiated(node, /*checkConstEnums*/ true)) { return undefined; } @@ -350,7 +350,7 @@ module ts.BreakpointResolver { function spanInBlock(block: Block): TypeScript.TextSpan { switch (block.parent.kind) { case SyntaxKind.ModuleDeclaration: - if (!isInstantiated(block.parent)) { + if (!isInstantiated(block.parent, /*checkConstEnums*/ true)) { return undefined; } @@ -407,7 +407,7 @@ module ts.BreakpointResolver { switch (node.parent.kind) { case SyntaxKind.ModuleBlock: // If this is not instantiated module block no bp span - if (!isInstantiated(node.parent.parent)) { + if (!isInstantiated(node.parent.parent, /*checkConstEnums*/ true)) { return undefined; } diff --git a/src/services/services.ts b/src/services/services.ts index d2e4360725d..0b14ad90c24 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4549,7 +4549,7 @@ module ts { if ((node).name.kind === SyntaxKind.StringLiteral) { return SemanticMeaning.Namespace | SemanticMeaning.Value; } - else if (isInstantiated(node)) { + else if (isInstantiated(node, /*checkConstEnums*/ true)) { return SemanticMeaning.Namespace | SemanticMeaning.Value; } else { @@ -4826,7 +4826,7 @@ module ts { */ function hasValueSideModule(symbol: Symbol): boolean { return forEach(symbol.declarations, declaration => { - return declaration.kind === SyntaxKind.ModuleDeclaration && isInstantiated(declaration); + return declaration.kind === SyntaxKind.ModuleDeclaration && isInstantiated(declaration, /*checkConstEnums*/ true); }); } } diff --git a/tests/baselines/reference/constEnumErrors.errors.txt b/tests/baselines/reference/constEnumErrors.errors.txt new file mode 100644 index 00000000000..2464180ad34 --- /dev/null +++ b/tests/baselines/reference/constEnumErrors.errors.txt @@ -0,0 +1,65 @@ +tests/cases/compiler/constEnumErrors.ts(1,12): error TS2300: Duplicate identifier 'E'. +tests/cases/compiler/constEnumErrors.ts(5,8): error TS2300: Duplicate identifier 'E'. +tests/cases/compiler/constEnumErrors.ts(12,9): error TS4083: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(14,9): error TS4083: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(15,10): error TS4083: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(22,13): error TS4085: Index expression arguments in 'const' enums must be of type 'string'. +tests/cases/compiler/constEnumErrors.ts(24,13): error TS4085: Index expression arguments in 'const' enums must be of type 'string'. +tests/cases/compiler/constEnumErrors.ts(26,9): error TS4084: 'const' enums can only be used in property access expressions. +tests/cases/compiler/constEnumErrors.ts(27,10): error TS4084: 'const' enums can only be used in property access expressions. +tests/cases/compiler/constEnumErrors.ts(32,5): error TS4084: 'const' enums can only be used in property access expressions. + + +==== tests/cases/compiler/constEnumErrors.ts (10 errors) ==== + const enum E { + ~ +!!! error TS2300: Duplicate identifier 'E'. + A + } + + module E { + ~ +!!! error TS2300: Duplicate identifier 'E'. + var x = 1; + } + + const enum E1 { + // illegal case + // forward reference to the element of the same enum + X = Y, + ~ +!!! error TS4083: In 'const' enum declarations member initializer must be constant expression. + // forward reference to the element of the same enum + Y = E1.Z, + ~~~~ +!!! error TS4083: In 'const' enum declarations member initializer must be constant expression. + Y1 = E1["Z"] + ~~~~~~~ +!!! error TS4083: In 'const' enum declarations member initializer must be constant expression. + } + + const enum E2 { + A + } + + var y0 = E2[1] + ~ +!!! error TS4085: Index expression arguments in 'const' enums must be of type 'string'. + var name = "A"; + var y1 = E2[name]; + ~~~~ +!!! error TS4085: Index expression arguments in 'const' enums must be of type 'string'. + + var x = E2; + ~~ +!!! error TS4084: 'const' enums can only be used in property access expressions. + var y = [E2]; + ~~ +!!! error TS4084: 'const' enums can only be used in property access expressions. + + function foo(t: any): void { + } + + foo(E2); + ~~ +!!! error TS4084: 'const' enums can only be used in property access expressions. \ No newline at end of file diff --git a/tests/baselines/reference/constEnumErrors.js b/tests/baselines/reference/constEnumErrors.js new file mode 100644 index 00000000000..7a1260583df --- /dev/null +++ b/tests/baselines/reference/constEnumErrors.js @@ -0,0 +1,47 @@ +//// [constEnumErrors.ts] +const enum E { + A +} + +module E { + var x = 1; +} + +const enum E1 { + // illegal case + // forward reference to the element of the same enum + X = Y, + // forward reference to the element of the same enum + Y = E1.Z, + Y1 = E1["Z"] +} + +const enum E2 { + A +} + +var y0 = E2[1] +var name = "A"; +var y1 = E2[name]; + +var x = E2; +var y = [E2]; + +function foo(t: any): void { +} + +foo(E2); + +//// [constEnumErrors.js] +var E; +(function (E) { + var x = 1; +})(E || (E = {})); +var y0 = E2[1]; +var name = "A"; +var y1 = E2[name]; +var x = E2; +var y = [E2]; +function foo(t) { +} +foo(E2); diff --git a/tests/baselines/reference/constantsInEnumMembers.js b/tests/baselines/reference/constEnums.js similarity index 65% rename from tests/baselines/reference/constantsInEnumMembers.js rename to tests/baselines/reference/constEnums.js index 62d6f9fbf3c..aa19b8e9230 100644 --- a/tests/baselines/reference/constantsInEnumMembers.js +++ b/tests/baselines/reference/constEnums.js @@ -1,4 +1,4 @@ -//// [constantsInEnumMembers.ts] +//// [constEnums.ts] const enum Enum1 { A0 = 100, } @@ -8,14 +8,14 @@ const enum Enum1 { 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, + D = A | B, + E = A | 1, + F = 1 | A, + G = (1 & 1), + H = ~(A | B), + I = A >>> 1, + J = 1 & A, + K = ~(1 | 5), L = ~D, M = E << B, N = E << 1, @@ -34,13 +34,6 @@ const enum Enum1 { W2 = Enum1.A0, W3 = Enum1["A0"], W4 = Enum1["W"], - // illegal case - // forward reference to the element of the same enum - X = Y, - // forward reference to the element of the same enum - Y = Enum1.Z, - Y1 = Enum1["Z"], - Z = 100, } @@ -49,7 +42,7 @@ module A { export module C { export const enum E { V1 = 1, - V2 = A.B.C.E.V1 + 100 + V2 = A.B.C.E.V1 | 100 } } } @@ -59,12 +52,21 @@ module A { export module B { export module C { export const enum E { - V3 = A.B.C.E["V2"] + 200, + V3 = A.B.C.E["V2"] & 200, } } } } +import I = A.B.C.E; + +function foo0(e: I): void { + if (e === I.V1) { + } + else if (e === I.V2) { + } +} + function foo(x: Enum1) { switch (x) { case Enum1.A: @@ -94,10 +96,6 @@ function foo(x: Enum1) { case Enum1.W2: case Enum1.W3: case Enum1.W4: - case Enum1.X: - case Enum1.Y: - case Enum1.Y1: - case Enum1.Z: break; } } @@ -110,25 +108,13 @@ function bar(e: A.B.C.E): number { } } -//// [constantsInEnumMembers.js] -var A; -(function (A) { - var B; - (function (B) { - var C; - (function (C) { - })(C = B.C || (B.C = {})); - })(B = A.B || (A.B = {})); -})(A || (A = {})); -var A; -(function (A) { - var B; - (function (B) { - var C; - (function (C) { - })(C = B.C || (B.C = {})); - })(B = A.B || (A.B = {})); -})(A || (A = {})); +//// [constEnums.js] +function foo0(e) { + if (e === 1 /* V1 */) { + } + else if (e === 101 /* V2 */) { + } +} function foo(x) { switch (x) { case 0 /* A */: @@ -137,11 +123,11 @@ function foo(x) { case 1 /* D */: case 1 /* E */: case 1 /* F */: - case 2 /* G */: - case -1 /* H */: - case -1 /* I */: - case 1 /* J */: - case 0 /* K */: + case 1 /* G */: + case -2 /* H */: + case 0 /* I */: + case 0 /* J */: + case -6 /* K */: case -2 /* L */: case 2 /* M */: case 2 /* N */: @@ -158,10 +144,6 @@ function foo(x) { case 100 /* W2 */: case 100 /* W3 */: case 11 /* W4 */: - case Enum1.X: - case Enum1.Y: - case Enum1.Y1: - case 100 /* Z */: break; } } @@ -171,7 +153,7 @@ function bar(e) { return 1; case 101 /* V2 */: return 1; - case 301 /* V3 */: + case 64 /* V3 */: return 1; } } diff --git a/tests/baselines/reference/constEnums.types b/tests/baselines/reference/constEnums.types new file mode 100644 index 00000000000..3ce0eeac9e3 --- /dev/null +++ b/tests/baselines/reference/constEnums.types @@ -0,0 +1,433 @@ +=== tests/cases/compiler/constEnums.ts === +const enum Enum1 { +>Enum1 : Enum1 + + A0 = 100, +>A0 : Enum1 +} + +const 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 +>1 & 1 : number + + H = ~(A | B), +>H : Enum1 +>~(A | B) : number +>(A | B) : number +>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 | 5), +>K : Enum1 +>~(1 | 5) : number +>(1 | 5) : number +>1 | 5 : 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 +} + + +module A { +>A : typeof A + + export module B { +>B : typeof B + + export module C { +>C : typeof C + + export const 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 const 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 + } + } + } +} + +import I = A.B.C.E; +>I : typeof I +>A : typeof A +>B : typeof A.B +>C : typeof A.B.C +>E : I + +function foo0(e: I): void { +>foo0 : (e: I) => void +>e : I +>I : I + + if (e === I.V1) { +>e === I.V1 : boolean +>e : I +>I.V1 : I +>I : typeof I +>V1 : I + } + else if (e === I.V2) { +>e === I.V2 : boolean +>e : I +>I.V2 : I +>I : typeof I +>V2 : I + } +} + +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 + + break; + } +} + +function bar(e: A.B.C.E): number { +>bar : (e: I) => number +>e : I +>A : unknown +>B : unknown +>C : unknown +>E : I + + switch (e) { +>e : I + + case A.B.C.E.V1: return 1; +>A.B.C.E.V1 : I +>A.B.C.E : typeof I +>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 I +>V1 : I + + case A.B.C.E.V2: return 1; +>A.B.C.E.V2 : I +>A.B.C.E : typeof I +>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 I +>V2 : I + + case A.B.C.E.V3: return 1; +>A.B.C.E.V3 : I +>A.B.C.E : typeof I +>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 I +>V3 : I + } +} diff --git a/tests/baselines/reference/constantsInEnumMembers.errors.txt b/tests/baselines/reference/constantsInEnumMembers.errors.txt deleted file mode 100644 index 323a352ebb2..00000000000 --- a/tests/baselines/reference/constantsInEnumMembers.errors.txt +++ /dev/null @@ -1,122 +0,0 @@ -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/cases/compiler/constEnumErrors.ts b/tests/cases/compiler/constEnumErrors.ts new file mode 100644 index 00000000000..b77a4399284 --- /dev/null +++ b/tests/cases/compiler/constEnumErrors.ts @@ -0,0 +1,32 @@ +const enum E { + A +} + +module E { + var x = 1; +} + +const enum E1 { + // illegal case + // forward reference to the element of the same enum + X = Y, + // forward reference to the element of the same enum + Y = E1.Z, + Y1 = E1["Z"] +} + +const enum E2 { + A +} + +var y0 = E2[1] +var name = "A"; +var y1 = E2[name]; + +var x = E2; +var y = [E2]; + +function foo(t: any): void { +} + +foo(E2); \ No newline at end of file diff --git a/tests/cases/compiler/constantsInEnumMembers.ts b/tests/cases/compiler/constEnums.ts similarity index 72% rename from tests/cases/compiler/constantsInEnumMembers.ts rename to tests/cases/compiler/constEnums.ts index dfaac3ffe0a..3198b25d51f 100644 --- a/tests/cases/compiler/constantsInEnumMembers.ts +++ b/tests/cases/compiler/constEnums.ts @@ -7,14 +7,14 @@ const enum Enum1 { 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, + D = A | B, + E = A | 1, + F = 1 | A, + G = (1 & 1), + H = ~(A | B), + I = A >>> 1, + J = 1 & A, + K = ~(1 | 5), L = ~D, M = E << B, N = E << 1, @@ -33,13 +33,6 @@ const enum Enum1 { W2 = Enum1.A0, W3 = Enum1["A0"], W4 = Enum1["W"], - // illegal case - // forward reference to the element of the same enum - X = Y, - // forward reference to the element of the same enum - Y = Enum1.Z, - Y1 = Enum1["Z"], - Z = 100, } @@ -48,7 +41,7 @@ module A { export module C { export const enum E { V1 = 1, - V2 = A.B.C.E.V1 + 100 + V2 = A.B.C.E.V1 | 100 } } } @@ -58,12 +51,21 @@ module A { export module B { export module C { export const enum E { - V3 = A.B.C.E["V2"] + 200, + V3 = A.B.C.E["V2"] & 200, } } } } +import I = A.B.C.E; + +function foo0(e: I): void { + if (e === I.V1) { + } + else if (e === I.V2) { + } +} + function foo(x: Enum1) { switch (x) { case Enum1.A: @@ -93,10 +95,6 @@ function foo(x: Enum1) { case Enum1.W2: case Enum1.W3: case Enum1.W4: - case Enum1.X: - case Enum1.Y: - case Enum1.Y1: - case Enum1.Z: break; } }