diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8fbea0dd367..e391257c64e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -18,6 +18,8 @@ namespace ts { NamespaceExports = 1 << 1, /** Enables substitutions for async methods with `super` calls. */ AsyncMethodsWithSuper = 1 << 2, + /* Enables substitutions for unqualified enum members */ + NonQualifiedEnumMembers = 1 << 3 } export function transformTypeScript(context: TransformationContext) { @@ -70,7 +72,7 @@ namespace ts { * Keeps track of whether we are within any containing namespaces when performing * just-in-time substitution while printing an expression identifier. */ - let isEnclosedInNamespace: boolean; + let applicableSubstitutions: TypeScriptSubstitutionFlags; /** * This keeps track of containers where `super` is valid, for use with @@ -2263,26 +2265,29 @@ namespace ts { // ... // })(x || (x = {})); statements.push( - setOriginalNode( - createStatement( - createCall( - createFunctionExpression( + setNodeEmitFlags( + setOriginalNode( + createStatement( + createCall( + createFunctionExpression( /*asteriskToken*/ undefined, /*name*/ undefined, - [createParameter(localName)], - transformEnumBody(node, localName) - ), - [createLogicalOr( - name, - createAssignment( + [createParameter(localName)], + transformEnumBody(node, localName) + ), + [createLogicalOr( name, - createObjectLiteral() - ) - )] - ), + createAssignment( + name, + createObjectLiteral() + ) + )] + ), /*location*/ node - ), + ), /*original*/ node + ), + NodeEmitFlags.AdviseOnEmitNode ) ); @@ -2346,11 +2351,14 @@ namespace ts { if (value !== undefined) { return createLiteral(value); } - else if (member.initializer) { - return visitNode(member.initializer, visitor, isExpression); - } else { - return createVoidZero(); + enableSubstitutionForNonQualifiedEnumMembers(); + if (member.initializer) { + return visitNode(member.initializer, visitor, isExpression); + } + else { + return createVoidZero(); + } } } @@ -2663,8 +2671,12 @@ namespace ts { return getOriginalNode(node).kind === SyntaxKind.ModuleDeclaration; } + function isTransformedEnumDeclaration(node: Node): boolean { + return getOriginalNode(node).kind === SyntaxKind.EnumDeclaration; + } + function onEmitNode(node: Node, emit: (node: Node) => void): void { - const savedIsEnclosedInNamespace = isEnclosedInNamespace; + const savedApplicableSubstitutions = applicableSubstitutions; const savedCurrentSuperContainer = currentSuperContainer; // If we need support substitutions for aliases for decorated classes, @@ -2680,7 +2692,10 @@ namespace ts { } if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) { - isEnclosedInNamespace = true; + applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports; + } + if (enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) { + applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; } previousOnEmitNode(node, emit); @@ -2689,7 +2704,7 @@ namespace ts { currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined; } - isEnclosedInNamespace = savedIsEnclosedInNamespace; + applicableSubstitutions = savedApplicableSubstitutions; currentSuperContainer = savedCurrentSuperContainer; } @@ -2734,18 +2749,22 @@ namespace ts { } } - if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isEnclosedInNamespace) { + if (enabledSubstitutions & applicableSubstitutions) { // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. const original = getOriginalNode(node); if (isIdentifier(original) && original.parent) { const container = resolver.getReferencedExportContainer(original); - if (container && container.kind === SyntaxKind.ModuleDeclaration) { - return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node); + if (container) { + const substitute = + (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || + (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration); + if (substitute) { + return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node); + } } } } - return node; } @@ -2799,6 +2818,13 @@ namespace ts { return node; } + function enableSubstitutionForNonQualifiedEnumMembers() { + if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers) === 0) { + enabledSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + } + } + function enableExpressionSubstitutionForAsyncMethodsWithSuper() { if ((enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) === 0) { enabledSubstitutions |= TypeScriptSubstitutionFlags.AsyncMethodsWithSuper; diff --git a/tests/baselines/reference/enumWithComputedMember.errors.txt b/tests/baselines/reference/enumWithComputedMember.errors.txt new file mode 100644 index 00000000000..cdbcaf575e8 --- /dev/null +++ b/tests/baselines/reference/enumWithComputedMember.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/enumWithComputedMember.ts(4,5): error TS1061: Enum member must have initializer. + + +==== tests/cases/compiler/enumWithComputedMember.ts (1 errors) ==== + enum A { + X = "".length, + Y = X, + Z + ~ +!!! error TS1061: Enum member must have initializer. + } + \ No newline at end of file diff --git a/tests/baselines/reference/enumWithComputedMember.js b/tests/baselines/reference/enumWithComputedMember.js new file mode 100644 index 00000000000..ca4bfac7adb --- /dev/null +++ b/tests/baselines/reference/enumWithComputedMember.js @@ -0,0 +1,15 @@ +//// [enumWithComputedMember.ts] +enum A { + X = "".length, + Y = X, + Z +} + + +//// [enumWithComputedMember.js] +var A; +(function (A) { + A[A["X"] = "".length] = "X"; + A[A["Y"] = A.X] = "Y"; + A[A["Z"] = void 0] = "Z"; +})(A || (A = {})); diff --git a/tests/cases/compiler/enumWithComputedMember.ts b/tests/cases/compiler/enumWithComputedMember.ts new file mode 100644 index 00000000000..10c7b1994a9 --- /dev/null +++ b/tests/cases/compiler/enumWithComputedMember.ts @@ -0,0 +1,5 @@ +enum A { + X = "".length, + Y = X, + Z +}