diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index a430476958e..734fbcaeb16 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3780,7 +3780,7 @@ namespace ts { function substituteExpressionIdentifier(node: Identifier): Identifier { if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) { const declaration = resolver.getReferencedDeclarationWithCollidingName(node); - if (declaration) { + if (declaration && !(isClassLike(declaration) && isPartOfClassBody(declaration, node))) { return setTextRange(getGeneratedNameForNode(declaration.name), node); } } @@ -3788,6 +3788,32 @@ namespace ts { return node; } + function isPartOfClassBody(declaration: ClassLikeDeclaration, node: Identifier) { + let currentNode = getParseTreeNode(node); + if (!currentNode || currentNode === declaration || currentNode.end <= declaration.pos || currentNode.pos >= declaration.end) { + // if the node has no correlation to a parse tree node, its definitely not + // part of the body. + // if the node is outside of the document range of the declaration, its + // definitely not part of the body. + return false; + } + const blockScope = getEnclosingBlockScopeContainer(declaration); + while (currentNode) { + if (currentNode === blockScope || currentNode === declaration) { + // if we are in the enclosing block scope of the declaration, we are definitely + // not inside the class body. + return false; + } + if (isClassElement(currentNode) && currentNode.parent === declaration) { + // we are in the class body, but we treat static fields as outside of the class body + return currentNode.kind !== SyntaxKind.PropertyDeclaration + || (getModifierFlags(currentNode) & ModifierFlags.Static) === 0; + } + currentNode = currentNode.parent; + } + return false; + } + /** * Substitutes `this` when contained within an arrow function. * @@ -3803,7 +3829,7 @@ namespace ts { function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { return hasModifier(member, ModifierFlags.Static) - ? getLocalName(node) + ? getInternalName(node) : createPropertyAccess(getInternalName(node), "prototype"); } diff --git a/tests/baselines/reference/classBlockScoping.js b/tests/baselines/reference/classBlockScoping.js new file mode 100644 index 00000000000..76d0dd892de --- /dev/null +++ b/tests/baselines/reference/classBlockScoping.js @@ -0,0 +1,71 @@ +//// [classBlockScoping.ts] +function f(b: boolean) { + let Foo: any; + if (b) { + Foo = class Foo { + static y = new Foo(); + + static x() { + new Foo(); + } + + m() { + new Foo(); + } + }; + + new Foo(); + } + else { + class Foo { + static y = new Foo(); + + static x() { + new Foo(); + } + + m() { + new Foo(); + } + } + + new Foo(); + } +} + +//// [classBlockScoping.js] +function f(b) { + var Foo; + if (b) { + Foo = (_a = (function () { + function Foo() { + } + Foo.x = function () { + new Foo(); + }; + Foo.prototype.m = function () { + new Foo(); + }; + return Foo; + }()), + _a.y = new _a(), + _a); + new Foo(); + } + else { + var Foo_1 = (function () { + function Foo() { + } + Foo.x = function () { + new Foo(); + }; + Foo.prototype.m = function () { + new Foo(); + }; + return Foo; + }()); + Foo_1.y = new Foo_1(); + new Foo_1(); + } + var _a; +} diff --git a/tests/baselines/reference/classBlockScoping.symbols b/tests/baselines/reference/classBlockScoping.symbols new file mode 100644 index 00000000000..571d54f2d8e --- /dev/null +++ b/tests/baselines/reference/classBlockScoping.symbols @@ -0,0 +1,64 @@ +=== tests/cases/compiler/classBlockScoping.ts === +function f(b: boolean) { +>f : Symbol(f, Decl(classBlockScoping.ts, 0, 0)) +>b : Symbol(b, Decl(classBlockScoping.ts, 0, 11)) + + let Foo: any; +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 1, 5)) + + if (b) { +>b : Symbol(b, Decl(classBlockScoping.ts, 0, 11)) + + Foo = class Foo { +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 1, 5)) +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9)) + + static y = new Foo(); +>y : Symbol(Foo.y, Decl(classBlockScoping.ts, 3, 21)) +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9)) + + static x() { +>x : Symbol(Foo.x, Decl(classBlockScoping.ts, 4, 27)) + + new Foo(); +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9)) + } + + m() { +>m : Symbol(Foo.m, Decl(classBlockScoping.ts, 8, 7)) + + new Foo(); +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9)) + } + }; + + new Foo(); +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 1, 5)) + } + else { + class Foo { +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8)) + + static y = new Foo(); +>y : Symbol(Foo.y, Decl(classBlockScoping.ts, 18, 15)) +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8)) + + static x() { +>x : Symbol(Foo.x, Decl(classBlockScoping.ts, 19, 27)) + + new Foo(); +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8)) + } + + m() { +>m : Symbol(Foo.m, Decl(classBlockScoping.ts, 23, 7)) + + new Foo(); +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8)) + } + } + + new Foo(); +>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8)) + } +} diff --git a/tests/baselines/reference/classBlockScoping.types b/tests/baselines/reference/classBlockScoping.types new file mode 100644 index 00000000000..a54499d7400 --- /dev/null +++ b/tests/baselines/reference/classBlockScoping.types @@ -0,0 +1,74 @@ +=== tests/cases/compiler/classBlockScoping.ts === +function f(b: boolean) { +>f : (b: boolean) => void +>b : boolean + + let Foo: any; +>Foo : any + + if (b) { +>b : boolean + + Foo = class Foo { +>Foo = class Foo { static y = new Foo(); static x() { new Foo(); } m() { new Foo(); } } : typeof Foo +>Foo : any +>class Foo { static y = new Foo(); static x() { new Foo(); } m() { new Foo(); } } : typeof Foo +>Foo : typeof Foo + + static y = new Foo(); +>y : Foo +>new Foo() : Foo +>Foo : typeof Foo + + static x() { +>x : () => void + + new Foo(); +>new Foo() : Foo +>Foo : typeof Foo + } + + m() { +>m : () => void + + new Foo(); +>new Foo() : Foo +>Foo : typeof Foo + } + }; + + new Foo(); +>new Foo() : any +>Foo : any + } + else { + class Foo { +>Foo : Foo + + static y = new Foo(); +>y : Foo +>new Foo() : Foo +>Foo : typeof Foo + + static x() { +>x : () => void + + new Foo(); +>new Foo() : Foo +>Foo : typeof Foo + } + + m() { +>m : () => void + + new Foo(); +>new Foo() : Foo +>Foo : typeof Foo + } + } + + new Foo(); +>new Foo() : Foo +>Foo : typeof Foo + } +} diff --git a/tests/cases/compiler/classBlockScoping.ts b/tests/cases/compiler/classBlockScoping.ts new file mode 100644 index 00000000000..d4620fc89c8 --- /dev/null +++ b/tests/cases/compiler/classBlockScoping.ts @@ -0,0 +1,33 @@ +function f(b: boolean) { + let Foo: any; + if (b) { + Foo = class Foo { + static y = new Foo(); + + static x() { + new Foo(); + } + + m() { + new Foo(); + } + }; + + new Foo(); + } + else { + class Foo { + static y = new Foo(); + + static x() { + new Foo(); + } + + m() { + new Foo(); + } + } + + new Foo(); + } +} \ No newline at end of file